[
  {
    "path": "12306.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\n@author: liuyw\n\"\"\"\nfrom splinter.browser import Browser\nfrom time import sleep\nimport traceback\nimport time, sys\n\nclass huoche(object):\n\tdriver_name = ''\n\texecutable_path = ''\n\t#用户名，密码\n\tusername = u\"xxx\"\n\tpasswd = u\"xxx\"\n\t# cookies值得自己去找, 下面两个分别是沈阳, 哈尔滨\n\tstarts = u\"%u6C88%u9633%2CSYT\"\n\tends = u\"%u54C8%u5C14%u6EE8%2CHBB\"\n\t\n\t# 时间格式2018-01-19\n\tdtime = u\"2018-01-19\"\n\t# 车次，选择第几趟，0则从上之下依次点击\n\torder = 0\n\t###乘客名\n\tusers = [u\"xxx\",u\"xxx\"]\n\t##席位\n\txb = u\"二等座\"\n\tpz = u\"成人票\"\n\n\t\"\"\"网址\"\"\"\n\tticket_url = \"https://kyfw.12306.cn/otn/leftTicket/init\"\n\tlogin_url = \"https://kyfw.12306.cn/otn/login/init\"\n\tinitmy_url = \"https://kyfw.12306.cn/otn/index/initMy12306\"\n\tbuy = \"https://kyfw.12306.cn/otn/confirmPassenger/initDc\"\n\t\n\tdef __init__(self):\n\t\tself.driver_name = 'chrome'\n\t\tself.executable_path = 'D:/chromedriver'\n\n\tdef login(self):\n\t\tself.driver.visit(self.login_url)\n\t\tself.driver.fill(\"loginUserDTO.user_name\", self.username)\n\t\t# sleep(1)\n\t\tself.driver.fill(\"userDTO.password\", self.passwd)\n\t\tprint(u\"等待验证码，自行输入...\")\n\t\twhile True:\n\t\t\tif self.driver.url != self.initmy_url:\n\t\t\t\tsleep(1)\n\t\t\telse:\n\t\t\t\tbreak\n\n\tdef start(self):\n\t\tself.driver = Browser(driver_name=self.driver_name,executable_path=self.executable_path)\n\t\tself.driver.driver.set_window_size(1400, 1000)\n\t\tself.login()\n\t\t# sleep(1)\n\t\tself.driver.visit(self.ticket_url)\n\t\ttry:\n\t\t\tprint(u\"购票页面开始...\")\n\t\t\t# sleep(1)\n\t\t\t# 加载查询信息\n\t\t\tself.driver.cookies.add({\"_jc_save_fromStation\": self.starts})\n\t\t\tself.driver.cookies.add({\"_jc_save_toStation\": self.ends})\n\t\t\tself.driver.cookies.add({\"_jc_save_fromDate\": self.dtime})\n\n\t\t\tself.driver.reload()\n\n\t\t\tcount = 0\n\t\t\tif self.order != 0:\n\t\t\t\twhile self.driver.url == self.ticket_url:\n\t\t\t\t\tself.driver.find_by_text(u\"查询\").click()\n\t\t\t\t\tcount += 1\n\t\t\t\t\tprint(u\"循环点击查询... 第 %s 次\" % count)\n\t\t\t\t\t# sleep(1)\n\t\t\t\t\ttry:\n\t\t\t\t\t\tself.driver.find_by_text(u\"预订\")[self.order - 1].click()\n\t\t\t\t\texcept Exception as e:\n\t\t\t\t\t\tprint(e)\n\t\t\t\t\t\tprint(u\"还没开始预订\")\n\t\t\t\t\t\tcontinue\n\t\t\telse:\n\t\t\t\twhile self.driver.url == self.ticket_url:\n\t\t\t\t\tself.driver.find_by_text(u\"查询\").click()\n\t\t\t\t\tcount += 1\n\t\t\t\t\tprint(u\"循环点击查询... 第 %s 次\" % count)\n\t\t\t\t\t# sleep(0.8)\n\t\t\t\t\ttry:\n\t\t\t\t\t\tfor i in self.driver.find_by_text(u\"预订\"):\n\t\t\t\t\t\t\ti.click()\n\t\t\t\t\t\t\tsleep(1)\n\t\t\t\t\texcept Exception as e:\n\t\t\t\t\t\tprint(e)\n\t\t\t\t\t\tprint(u\"还没开始预订 %s\" % count)\n\t\t\t\t\t\tcontinue\n\t\t\tprint(u\"开始预订...\")\n\t\t\t# sleep(3)\n\t\t\t# self.driver.reload()\n\t\t\tsleep(1)\n\t\t\tprint(u'开始选择用户...')\n\t\t\tfor user in self.users:\n\t\t\t\tself.driver.find_by_text(user).last.click()\n\n\t\t\tprint(u\"提交订单...\")\n\t\t\tsleep(1)\n\t\t\tself.driver.find_by_text(self.pz).click()\n\t\t\tself.driver.find_by_id('').select(self.pz)\n\t\t\t# sleep(1)\n\t\t\tself.driver.find_by_text(self.xb).click()\n\t\t\tsleep(1)\n\t\t\tself.driver.find_by_id('submitOrder_id').click()\n\t\t\tprint(u\"开始选座...\")\n\t\t\tself.driver.find_by_id('1D').last.click()\n\t\t\tself.driver.find_by_id('1F').last.click()\n\n\t\t\tsleep(1.5)\n\t\t\tprint(u\"确认选座...\")\n\t\t\tself.driver.find_by_id('qr_submit_id').click()\n\n\t\texcept Exception as e:\n\t\t\tprint(e)\n\nif __name__ == '__main__':\n\thuoche = huoche()\n\thuoche.start()"
  },
  {
    "path": "2020/README.md",
    "content": "# Python Spider 2020\n\n由于这个项目时间太长了，陆陆续续，很多实战示例也早已失效。\n\n网络爬虫，是一门比较通用的基础技术，各个领域都会有所涉及，比如我做视觉算法的，也需要用到网络爬虫，例如调用 API 接口清洗数据等，这本质也都是一个小的爬虫程序。\n\n为了提供各位更好的学习示例，我决定重写这一系列教程，对一些失效的示例，重新找例子，并查缺补漏，完善这一些列教程。\n\n2020年，最新版的 Python3 网络爬虫实战系列教程。\n\n原创文章每周最少两篇，**后续最新文章**会在[【公众号】](https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg)首发，视频[【B站】](https://space.bilibili.com/331507846)首发，大家可以加我[【微信】](https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg)进**交流群**，技术交流或提意见都可以，欢迎**Star**！\n\n<p align=\"center\">\n  <a href=\"https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg\" target=\"_blank\"><img src=\"https://img.shields.io/badge/weChat-微信群-blue.svg\" alt=\"微信群\"></a>\n  <a href=\"https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg\" target=\"_blank\"><img src=\"https://img.shields.io/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-Jack%20Cui-lightgrey.svg\" alt=\"公众号\"></a>\n  <a href=\"https://space.bilibili.com/331507846\"><img src=\"https://img.shields.io/badge/bilibili-哔哩哔哩-critical\" alt=\"B站\"></a>\n  <a href=\"https://www.zhihu.com/people/Jack--Cui\" target=\"_blank\"><img src=\"https://img.shields.io/badge/zhihu-知乎-informational\" alt=\"知乎\"></a>\n  <a href=\"https://blog.csdn.net/c406495762\" target=\"_blank\"><img src=\"https://img.shields.io/badge/csdn-CSDN-red.svg\" alt=\"CSDN\"></a>\n  <a href=\"https://www.toutiao.com/c/user/token/MS4wLjABAAAA5gJtmezUJ6vli2hZvnN13iLnzKLpuF8gGHeS0iVlmNs/\" target=\"_blank\"><img src=\"https://img.shields.io/badge/toutiao-%E5%A4%B4%E6%9D%A1-important.svg\" alt=\"头条\"></a>\n  <a href=\"https://juejin.im/user/5ea2ca74e51d4546b50d5f9f\" target=\"_blank\"><img src=\"https://img.shields.io/badge/juejin-掘金-blue.svg\" alt=\"掘金\"></a>\n</p>\n\n## Python3 网络爬虫教程 2020\n|   文章   |  公众号  |    代码    |\n| :------  | :--------: | :--------: |\n| Python3 网络爬虫（一）：初识网络爬虫之夜探老王家 | [公众号](https://mp.weixin.qq.com/s/1rcq9RQYuAuHFg1w1j8HXg \"Python3 网络爬虫（一）\") | no |\n| Python3 网络爬虫（二）：下载小说的正确姿势 | [公众号](https://mp.weixin.qq.com/s/5e2_r0QXUISVp9GdDsqbzg \"Python3 网络爬虫（二）\") | [Code](https://github.com/Jack-Cherish/python-spider/tree/master/2020/xbqg \"Python3 网络爬虫（二）\") |\n| Python3 网络爬虫（三）：漫画下载，动态加载、反爬虫这都不叫事！| [公众号](https://mp.weixin.qq.com/s/wyS-OP04K3Vs9arSelRlyA \"Python3网络爬虫（三）\") | [Code](https://github.com/Jack-Cherish/python-spider/tree/master/2020/dmzj \"Python3 网络爬虫（三）\") |\n| Python3 网络爬虫（四）：视频下载，那些事儿！| [公众号](https://mp.weixin.qq.com/s/_geNA6Dwo4kx25X7trJzlg \"Python3 网络爬虫（四）\") | [Code](https://github.com/Jack-Cherish/python-spider/tree/master/2020/zycjw \"Python3 网络爬虫（四）\") |\n| Python3 网络爬虫（五）：老板，需要特殊服务吗？| [公众号](https://mp.weixin.qq.com/s/PPTSnIHV71b-wB3oRiYnIA \"Python3 网络爬虫（五）\") | [Code](https://github.com/Jack-Cherish/python-spider/tree/master/2020/api \"Python3 网络爬虫（五）\") |\n| Python3 网络爬虫（六）：618，爱他/她，就清空他/她的购物车！| [公众号](https://mp.weixin.qq.com/s/lXXDfzyLVrf3f-aqJN1C3A \"Python3 网络爬虫（六）\") | [Code](https://github.com/Jack-Cherish/python-spider/tree/master/2020/taobao \"Python3 网络爬虫（六）\") |\n| 宝藏B站UP主，视频弹幕尽收囊中！| [公众号](https://mp.weixin.qq.com/s/aWratg1j9RBAjIghoY66yQ \"宝藏B站UP主，视频弹幕尽收囊中!\") | [Code](https://github.com/Jack-Cherish/python-spider/tree/master/2020/bilibili \"宝藏B站UP主，视频弹幕尽收囊中!\") |\n\n更多精彩，敬请期待！\n\n<a name=\"微信\"></a>  <a name=\"公众号\"></a>\n\n<img src=\"https://ftp.bmp.ovh/imgs/2020/07/112254f0199e3d4f.jpg\" alt=\"wechat\" width=\"400\" height=\"200\" align=\"bottom\" />\n"
  },
  {
    "path": "2020/api/api.py",
    "content": "import requests\nimport base64\nimport json\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\n%matplotlib inline\n \n \nbeautify_url = \"https://api-cn.faceplusplus.com/facepp/v2/beautify\"\n# 你创建的应用的 API Key 和 API Secret(也叫 Secret Key)\nAK = ''\nSK = ''\n \n# 可选参数，不填写，默认50\n# 美白程度 0 - 100\nwhitening = 80\n# 磨皮程度 0 - 100\nsmoothing = 80\n# 瘦脸程度 0 - 100\nthinface = 20\n# 小脸程度 0 - 100\nshrink_face = 50\n# 大眼程度 0 - 100\nenlarge_eye = 50\n# 去眉毛程度 0 - 100\nremove_eyebrow = 50\n# 滤镜名称，不填写，默认无滤镜\nfilter_type = ''\n \n# 二进制方式打开图片\nimg_name = 'test_1.png'\nf = open(img_name, 'rb')\n# 转 base64\nimg_base64 = base64.b64encode(f.read())\n \n# 使用 whitening、smoothing、thinface 三个可选参数，其他用默认值\ndata = {\n    'api_key': AK,\n    'api_secret': SK,\n    'image_base64': img_base64,\n    'whitening': whitening,\n    'smoothing': smoothing,\n    'thinface': thinface,\n    }\n \nr = requests.post(url=beautify_url, data=data)\nhtml = json.loads(r.text)\n \n# 解析base64图片\nbase64_data = html['result']\nimgData = base64.b64decode(base64_data)\nnparr = np.frombuffer(imgData, np.uint8)\nimg_res = cv2.imdecode(nparr, cv2.IMREAD_COLOR)\nimg_res_BGR = cv2.cvtColor(img_res, cv2.COLOR_RGB2BGR)\n \n# 原始图片\nimg = cv2.imread(img_name)\nimg_BGR = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)\n \n# 显示图片\nfig, axs = plt.subplots(nrows=1, ncols=2, sharex=False, sharey=False, figsize=(10,10))\naxs[0].imshow(img_BGR)\naxs[1].imshow(img_res_BGR)\nplt.show()\n"
  },
  {
    "path": "2020/bilibili/download.py",
    "content": "# -*-coding:utf-8 -*-\n# Website: https://cuijiahua.com\n# Author: Jack Cui\n# Date: 2020.07.22\nimport requests\nimport json\nimport re\nimport json\nimport math\nimport xml2ass\nimport time\nfrom contextlib import closing\n\nfrom bs4 import BeautifulSoup\n\nimport os\nfrom win32com.client import Dispatch\n\ndef addTasktoXunlei(down_url):\n    flag = False\n    o = Dispatch('ThunderAgent.Agent64.1')\n    try:\n        o.AddTask(down_url, \"\", \"\", \"\", \"\", -1, 0, 5)\n        o.CommitTasks()\n        flag = True\n    except Exception:\n        print(Exception.message)\n        print(\" AddTask is fail!\")\n    return flag\n\ndef get_download_url(arcurl):\n    # 微信搜索 JackCui-AI 关注公众号，后台回复「B 站」获取视频解析地址\n    jiexi_url = 'xxx'\n    payload = {'url': arcurl}\n    jiexi_req = requests.get(jiexi_url, params=payload)\n    jiexi_bf = BeautifulSoup(jiexi_req.text)\n    jiexi_dn_url = jiexi_bf.iframe.get('src')\n    dn_req = requests.get(jiexi_dn_url)\n    dn_bf = BeautifulSoup(dn_req.text)\n    video_script = dn_bf.find('script',src = None)\n    DPlayer = str(video_script.string)\n    download_url = re.findall('\\'(http[s]?:(?:[a-zA-Z]|[0-9]|[$-_@.&~+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)\\'', DPlayer)[0]\n    download_url = download_url.replace('\\\\', '')\n    return download_url\n\nspace_url = 'https://space.bilibili.com/280793434'\nsearch_url = 'https://api.bilibili.com/x/space/arc/search'\nmid = space_url.split('/')[-1]\nsess = requests.Session()\nsearch_headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n    'Accept-Language': 'zh-CN,zh;q=0.9',\n    'Accept-Encoding': 'gzip, deflate, br',\n    'Accept': 'application/json, text/plain, */*'}\n\n# 获取视频个数\nps = 1\npn = 1\nsearch_params = {'mid': mid,\n                 'ps': ps,\n                 'tid': 0,\n                 'pn': pn}\nreq = sess.get(url=search_url, headers=search_headers, params=search_params, verify=False)\ninfo = json.loads(req.text)\nvideo_count = info['data']['page']['count']\n\nps = 10\npage = math.ceil(video_count/ps)\nvideos_list = []\nfor pn in range(1, page+1):\n    search_params = {'mid': mid,\n                     'ps': ps,\n                     'tid': 0,\n                     'pn': pn}\n    req = sess.get(url=search_url, headers=search_headers, params=search_params, verify=False)\n    info = json.loads(req.text)\n    vlist = info['data']['list']['vlist']\n    for video in vlist:\n        title = video['title']\n        bvid = video['bvid']\n        vurl = 'https://www.bilibili.com/video/' + bvid\n        videos_list.append([title, vurl])\nprint('共 %d 个视频' % len(videos_list))\nall_video = {}\n# 下载前 10 个视频\nfor video in videos_list[:10]:\n    download_url = get_download_url(video[1])\n    print(video[0] + ':' + download_url)\n    # 记录视频名字\n    xunlei_video_name = download_url.split('?')[0].split('/')[-1]\n    filename = video[0]\n    for c in u'´☆<img draggable=\"false\" data-mce-resize=\"false\" data-mce-placeholder=\"1\" data-wp-emoji=\"1\" class=\"emoji\" alt=\"❤\" src=\"https://s.w.org/images/core/emoji/11.2.0/svg/2764.svg\">◦\\/:*?\"<>| ':\n        filename = filename.replace(c, '')\n    save_video_name = filename + '.mp4'\n    all_video[xunlei_video_name] = save_video_name\n\n    addTasktoXunlei(download_url)\n    # 弹幕下载\n    danmu_name = filename + '.xml'\n    danmu_ass = filename + '.ass'\n    oid = download_url.split('/')[6]\n    danmu_url = 'https://api.bilibili.com/x/v1/dm/list.so?oid={}'.format(oid)\n    danmu_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n                    'Accept': '*/*',\n                    'Accept-Encoding': 'gzip, deflate, br',\n                    'Accept-Language': 'zh-CN,zh;q=0.9'}\n    with closing(sess.get(danmu_url, headers=danmu_header, stream=True, verify=False)) as response:  \n        if response.status_code == 200:\n            with open(danmu_name, 'wb') as file:\n                for data in response.iter_content():\n                    file.write(data)\n                    file.flush()\n        else:\n            print('链接异常')\n    time.sleep(0.5)\n    xml2ass.Danmaku2ASS(danmu_name, danmu_ass, 1280, 720)\n# 视频重命名\nfor key, item in all_video.items():\n    while key not in os.listdir('./'):\n        time.sleep(1)\n    os.rename(key, item)\n"
  },
  {
    "path": "2020/bilibili/xml2ass.py",
    "content": "# The original author of this program, Danmaku2ASS, is StarBrilliant.\n# This file is released under General Public License version 3.\n# You should have received a copy of General Public License text alongside with\n# this program. If not, you can obtain it at http://gnu.org/copyleft/gpl.html .\n# This program comes with no warranty, the author will not be resopnsible for\n# any damage or problems caused by this program.\n\nimport argparse\nimport calendar\nimport gettext\nimport io\nimport json\nimport logging\nimport math\nimport os\nimport random\nimport re\nimport sys\nimport time\nimport xml.dom.minidom\n\n\nif sys.version_info < (3,):\n    raise RuntimeError('at least Python 3.0 is required')\n\ngettext.install('danmaku2ass', os.path.join(os.path.dirname(os.path.abspath(os.path.realpath(sys.argv[0] or 'locale'))), 'locale'))\n\ndef SeekZero(function):\n    def decorated_function(file_):\n        file_.seek(0)\n        try:\n            return function(file_)\n        finally:\n            file_.seek(0)\n    return decorated_function\n\n\ndef EOFAsNone(function):\n    def decorated_function(*args, **kwargs):\n        try:\n            return function(*args, **kwargs)\n        except EOFError:\n            return None\n    return decorated_function\n\n\n@SeekZero\n@EOFAsNone\ndef ProbeCommentFormat(f):\n    tmp = f.read(1)\n    if tmp == '[':\n        return 'Acfun'\n        # It is unwise to wrap a JSON object in an array!\n        # See this: http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/\n        # Do never follow what Acfun developers did!\n    elif tmp == '{':\n        tmp = f.read(14)\n        if tmp == '\"status_code\":':\n            return 'Tudou'\n        elif tmp == '\"root\":{\"total':\n            return 'sH5V'\n    elif tmp == '<':\n        tmp = f.read(1)\n        if tmp == '?':\n            tmp = f.read(38)\n            if tmp == 'xml version=\"1.0\" encoding=\"UTF-8\"?><p':\n                return 'Niconico'\n            elif tmp == 'xml version=\"1.0\" encoding=\"UTF-8\"?><i':\n                return 'Bilibili'\n            elif tmp == 'xml version=\"1.0\" encoding=\"utf-8\"?><i':\n                return 'Bilibili'  # tucao.cc, with the same file format as Bilibili\n            elif tmp == 'xml version=\"1.0\" encoding=\"Utf-8\"?>\\n<':\n                return 'Bilibili'  # Komica, with the same file format as Bilibili\n            elif tmp == 'xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<':\n                return 'MioMio'\n        elif tmp == 'p':\n            return 'Niconico'  # Himawari Douga, with the same file format as Niconico Douga\n\n\n#\n# ReadComments**** protocol\n#\n# Input:\n#     f:         Input file\n#     fontsize:  Default font size\n#\n# Output:\n#     yield a tuple:\n#         (timeline, timestamp, no, comment, pos, color, size, height, width)\n#     timeline:  The position when the comment is replayed\n#     timestamp: The UNIX timestamp when the comment is submitted\n#     no:        A sequence of 1, 2, 3, ..., used for sorting\n#     comment:   The content of the comment\n#     pos:       0 for regular moving comment,\n#                1 for bottom centered comment,\n#                2 for top centered comment,\n#                3 for reversed moving comment\n#     color:     Font color represented in 0xRRGGBB,\n#                e.g. 0xffffff for white\n#     size:      Font size\n#     height:    The estimated height in pixels\n#                i.e. (comment.count('\\n')+1)*size\n#     width:     The estimated width in pixels\n#                i.e. CalculateLength(comment)*size\n#\n# After implementing ReadComments****, make sure to update ProbeCommentFormat\n# and CommentFormatMap.\n#\n\n\ndef ReadCommentsNiconico(f, fontsize):\n    NiconicoColorMap = {'red': 0xff0000, 'pink': 0xff8080, 'orange': 0xffcc00, 'yellow': 0xffff00, 'green': 0x00ff00, 'cyan': 0x00ffff, 'blue': 0x0000ff, 'purple': 0xc000ff, 'black': 0x000000, 'niconicowhite': 0xcccc99, 'white2': 0xcccc99, 'truered': 0xcc0033, 'red2': 0xcc0033, 'passionorange': 0xff6600, 'orange2': 0xff6600, 'madyellow': 0x999900, 'yellow2': 0x999900, 'elementalgreen': 0x00cc66, 'green2': 0x00cc66, 'marineblue': 0x33ffcc, 'blue2': 0x33ffcc, 'nobleviolet': 0x6633cc, 'purple2': 0x6633cc}\n    dom = xml.dom.minidom.parse(f)\n    comment_element = dom.getElementsByTagName('chat')\n    for comment in comment_element:\n        try:\n            c = str(comment.childNodes[0].wholeText)\n            if c.startswith('/'):\n                continue  # ignore advanced comments\n            pos = 0\n            color = 0xffffff\n            size = fontsize\n            for mailstyle in str(comment.getAttribute('mail')).split():\n                if mailstyle == 'ue':\n                    pos = 1\n                elif mailstyle == 'shita':\n                    pos = 2\n                elif mailstyle == 'big':\n                    size = fontsize*1.44\n                elif mailstyle == 'small':\n                    size = fontsize*0.64\n                elif mailstyle in NiconicoColorMap:\n                    color = NiconicoColorMap[mailstyle]\n            yield (max(int(comment.getAttribute('vpos')), 0)*0.01, int(comment.getAttribute('date')), int(comment.getAttribute('no')), c, pos, color, size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %s') % comment.toxml())\n            continue\n\n\ndef ReadCommentsAcfun(f, fontsize):\n    comment_element = json.load(f)\n    for i, comment in enumerate(comment_element):\n        try:\n            p = str(comment['c']).split(',')\n            assert len(p) >= 6\n            assert p[2] in ('1', '2', '4', '5', '7')\n            size = int(p[3])*fontsize/25.0\n            if p[2] != '7':\n                c = str(comment['m']).replace('\\\\r', '\\n').replace('\\r', '\\n')\n                yield (float(p[0]), int(p[5]), i, c, {'1': 0, '2': 0, '4': 2, '5': 1}[p[2]], int(p[1]), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n            else:\n                c = dict(json.loads(comment['m']))\n                yield (float(p[0]), int(p[5]), i, c, 'acfunpos', int(p[1]), size, 0, 0)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %r') % comment)\n            continue\n\n\ndef ReadCommentsBilibili(f, fontsize):\n    dom = xml.dom.minidom.parse(f)\n    comment_element = dom.getElementsByTagName('d')\n    for i, comment in enumerate(comment_element):\n        try:\n            p = str(comment.getAttribute('p')).split(',')\n            assert len(p) >= 5\n            assert p[1] in ('1', '4', '5', '6', '7')\n            if p[1] != '7':\n                c = str(comment.childNodes[0].wholeText).replace('/n', '\\n')\n                size = int(p[2])*fontsize/25.0\n                yield (float(p[0]), int(p[4]), i, c, {'1': 0, '4': 2, '5': 1, '6': 3}[p[1]], int(p[3]), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n            else:  # positioned comment\n                c = str(comment.childNodes[0].wholeText)\n                yield (float(p[0]), int(p[4]), i, c, 'bilipos', int(p[3]), int(p[2]), 0, 0)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %s') % comment.toxml())\n            continue\n\n\ndef ReadCommentsTudou(f, fontsize):\n    comment_element = json.load(f)\n    for i, comment in enumerate(comment_element['comment_list']):\n        try:\n            assert comment['pos'] in (3, 4, 6)\n            c = str(comment['data'])\n            assert comment['size'] in (0, 1, 2)\n            size = {0: 0.64, 1: 1, 2: 1.44}[comment['size']]*fontsize\n            yield (int(comment['replay_time']*0.001), int(comment['commit_time']), i, c, {3: 0, 4: 2, 6: 1}[comment['pos']], int(comment['color']), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %r') % comment)\n            continue\n\n\ndef ReadCommentsMioMio(f, fontsize):\n    NiconicoColorMap = {'red': 0xff0000, 'pink': 0xff8080, 'orange': 0xffc000, 'yellow': 0xffff00, 'green': 0x00ff00, 'cyan': 0x00ffff, 'blue': 0x0000ff, 'purple': 0xc000ff, 'black': 0x000000}\n    dom = xml.dom.minidom.parse(f)\n    comment_element = dom.getElementsByTagName('data')\n    for i, comment in enumerate(comment_element):\n        try:\n            message = comment.getElementsByTagName('message')[0]\n            c = str(message.childNodes[0].wholeText)\n            pos = 0\n            size = int(message.getAttribute('fontsize'))*fontsize/25.0\n            yield (float(comment.getElementsByTagName('playTime')[0].childNodes[0].wholeText), int(calendar.timegm(time.strptime(comment.getElementsByTagName('times')[0].childNodes[0].wholeText, '%Y-%m-%d %H:%M:%S')))-28800, i, c, {'1': 0, '4': 2, '5': 1}[message.getAttribute('mode')], int(message.getAttribute('color')), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %s') % comment.toxml())\n            continue\n\n\ndef ReadCommentsSH5V(f, fontsize):\n    comment_element = json.load(f)\n    for i, comment in enumerate(comment_element[\"root\"][\"bgs\"]):\n        try:\n            c_at = str(comment['at'])\n            c_type = str(comment['type'])\n            c_date = str(comment['timestamp'])\n            c_color = str(comment['color'])\n            c = str(comment['text'])\n            size = fontsize\n            if c_type != '7':\n                yield (float(c_at), int(c_date), i, c, {'0': 0, '1': 0, '4': 2, '5': 1}[c_type], int(c_color[1:], 16), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n            else:\n                c_x = float(comment['x'])\n                c_y = float(comment['y'])\n                size = int(comment['size'])\n                dur = int(comment['dur'])\n                data1 = float(comment['data1'])\n                data2 = float(comment['data2'])\n                data3 = int(comment['data3'])\n                data4 = int(comment['data4'])\n                yield (float(c_at), int(c_date), i, c, 'sH5Vpos', int(c_color[1:], 16), size, 0, 0, c_x, c_y, dur, data1, data2, data3, data4)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %r') % comment)\n            continue\n\n\nCommentFormatMap = {None: None, 'Niconico': ReadCommentsNiconico, 'Acfun': ReadCommentsAcfun, 'Bilibili': ReadCommentsBilibili, 'Tudou': ReadCommentsTudou, 'MioMio': ReadCommentsMioMio, 'sH5V': ReadCommentsSH5V}\n\n\ndef WriteCommentBilibiliPositioned(f, c, width, height, styleid):\n    #BiliPlayerSize = (512, 384)  # Bilibili player version 2010\n    #BiliPlayerSize = (540, 384)  # Bilibili player version 2012\n    BiliPlayerSize = (672, 438)  # Bilibili player version 2014\n    ZoomFactor = GetZoomFactor(BiliPlayerSize, (width, height))\n\n    def GetPosition(InputPos, isHeight):\n        isHeight = int(isHeight)  # True -> 1\n        if isinstance(InputPos, int):\n            return ZoomFactor[0]*InputPos+ZoomFactor[isHeight+1]\n        elif isinstance(InputPos, float):\n            if InputPos > 1:\n                return ZoomFactor[0]*InputPos+ZoomFactor[isHeight+1]\n            else:\n                return BiliPlayerSize[isHeight]*ZoomFactor[0]*InputPos+ZoomFactor[isHeight+1]\n        else:\n            try:\n                InputPos = int(InputPos)\n            except ValueError:\n                InputPos = float(InputPos)\n            return GetPosition(InputPos, isHeight)\n\n    try:\n        comment_args = safe_list(json.loads(c[3]))\n        text = ASSEscape(str(comment_args[4]).replace('/n', '\\n'))\n        from_x = comment_args.get(0, 0)\n        from_y = comment_args.get(1, 0)\n        to_x = comment_args.get(7, from_x)\n        to_y = comment_args.get(8, from_y)\n        from_x = round(GetPosition(from_x, False))\n        from_y = round(GetPosition(from_y, True))\n        to_x = round(GetPosition(to_x, False))\n        to_y = round(GetPosition(to_y, True))\n        alpha = safe_list(str(comment_args.get(2, '1')).split('-'))\n        from_alpha = float(alpha.get(0, 1))\n        to_alpha = float(alpha.get(1, from_alpha))\n        from_alpha = 255-round(from_alpha*255)\n        to_alpha = 255-round(to_alpha*255)\n        rotate_z = int(comment_args.get(5, 0))\n        rotate_y = int(comment_args.get(6, 0))\n        lifetime = float(comment_args.get(3, 4500))\n        duration = int(comment_args.get(9, lifetime*1000))\n        delay = int(comment_args.get(10, 0))\n        fontface = comment_args.get(12)\n        isborder = comment_args.get(11, 'true')\n        styles = []\n        if (from_x, from_y) == (to_x, to_y):\n            styles.append('\\\\pos(%s, %s)' % (from_x, from_y))\n        else:\n            styles.append('\\\\move(%s, %s, %s, %s, %s, %s)' % (from_x, from_y, to_x, to_y, delay, delay+duration))\n        styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(rotate_y, rotate_z, (from_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (from_y-ZoomFactor[2])/(height-ZoomFactor[2]*2)))\n        if (from_x, from_y) != (to_x, to_y):\n            styles.append('\\\\t(%s, %s, ' % (delay, delay+duration))\n            styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(rotate_y, rotate_z, (to_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (to_y-ZoomFactor[2])/(height-ZoomFactor[2]*2)))\n            styles.append(')')\n        if fontface:\n            styles.append('\\\\fn%s' % ASSEscape(fontface))\n        styles.append('\\\\fs%s' % round(c[6]*ZoomFactor[0]))\n        if c[5] != 0xffffff:\n            styles.append('\\\\c&H%02X%02X%02X&' % (c[5] & 0xff, (c[5] >> 8) & 0xff, (c[5] >> 16) & 0xff))\n            if c[5] == 0x000000:\n                styles.append('\\\\3c&HFFFFFF&')\n        if from_alpha == to_alpha:\n            styles.append('\\\\alpha&H%02X' % from_alpha)\n        elif (from_alpha, to_alpha) == (255, 0):\n            styles.append('\\\\fad(%s,0)' % (lifetime*1000))\n        elif (from_alpha, to_alpha) == (0, 255):\n            styles.append('\\\\fad(0, %s)' % (lifetime*1000))\n        else:\n            styles.append('\\\\fade(%(from_alpha)s, %(to_alpha)s, %(to_alpha)s, 0, %(end_time)s, %(end_time)s, %(end_time)s)' % {'from_alpha': from_alpha, 'to_alpha': to_alpha, 'end_time': lifetime*1000})\n        if isborder == 'false':\n            styles.append('\\\\bord0')\n        f.write('Dialogue: -1,%(start)s,%(end)s,%(styleid)s,,0,0,0,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(c[0]), 'end': ConvertTimestamp(c[0]+lifetime), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n    except (IndexError, ValueError) as e:\n        try:\n            logging.warning(_('Invalid comment: %r') % c[3])\n        except IndexError:\n            logging.warning(_('Invalid comment: %r') % c)\n\n\ndef WriteCommentAcfunPositioned(f, c, width, height, styleid):\n    AcfunPlayerSize = (560, 400)\n    ZoomFactor = GetZoomFactor(AcfunPlayerSize, (width, height))\n\n    def GetPosition(InputPos, isHeight):\n        isHeight = int(isHeight)  # True -> 1\n        return AcfunPlayerSize[isHeight]*ZoomFactor[0]*InputPos*0.001+ZoomFactor[isHeight+1]\n\n    def GetTransformStyles(x=None, y=None, scale_x=None, scale_y=None, rotate_z=None, rotate_y=None, color=None, alpha=None):\n        styles = []\n        if x is not None and y is not None:\n            styles.append('\\\\pos(%s, %s)' % (x, y))\n        if scale_x is not None:\n            styles.append('\\\\fscx%s' % scale_x)\n        if scale_y is not None:\n            styles.append('\\\\fscy%s' % scale_y)\n        if rotate_z is not None and rotate_y is not None:\n            assert x is not None\n            assert y is not None\n            styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(rotate_y, rotate_z, (x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (y-ZoomFactor[2])/(height-ZoomFactor[2]*2)))\n        if color is not None:\n            styles.append('\\\\c&H%02X%02X%02X&' % (color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff))\n            if color == 0x000000:\n                styles.append('\\\\3c&HFFFFFF&')\n        if alpha is not None:\n            alpha = 255-round(alpha*255)\n            styles.append('\\\\alpha&H%02X' % alpha)\n        return styles\n\n    def FlushCommentLine(f, text, styles, start_time, end_time, styleid):\n        if end_time > start_time:\n            f.write('Dialogue: -1,%(start)s,%(end)s,%(styleid)s,,0,0,0,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(start_time), 'end': ConvertTimestamp(end_time), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n\n    try:\n        comment_args = c[3]\n        text = ASSEscape(str(comment_args['n']).replace('\\r', '\\n').replace('\\r', '\\n'))\n        common_styles = []\n        anchor = {0: 7, 1: 8, 2: 9, 3: 4, 4: 5, 5: 6, 6: 1, 7: 2, 8: 3}.get(comment_args.get('c', 0), 7)\n        if anchor != 7:\n            common_styles.append('\\\\an%s' % anchor)\n        font = comment_args.get('w')\n        if font:\n            font = dict(font)\n            fontface = font.get('f')\n            if fontface:\n                common_styles.append('\\\\fn%s' % ASSEscape(str(fontface)))\n            fontbold = bool(font.get('b'))\n            if fontbold:\n                common_styles.append('\\\\b1')\n        common_styles.append('\\\\fs%s' % round(c[6]*ZoomFactor[0]))\n        isborder = bool(comment_args.get('b', True))\n        if not isborder:\n            common_styles.append('\\\\bord0')\n        to_pos = dict(comment_args.get('p', {'x': 0, 'y': 0}))\n        to_x = round(GetPosition(int(to_pos.get('x', 0)), False))\n        to_y = round(GetPosition(int(to_pos.get('y', 0)), True))\n        to_scale_x = round(float(comment_args.get('e', 1.0))*100)\n        to_scale_y = round(float(comment_args.get('f', 1.0))*100)\n        to_rotate_z = float(comment_args.get('r', 0.0))\n        to_rotate_y = float(comment_args.get('k', 0.0))\n        to_color = c[5]\n        to_alpha = float(comment_args.get('a', 1.0))\n        from_time = float(comment_args.get('t', 0.0))\n        action_time = float(comment_args.get('l', 3.0))\n        actions = list(comment_args.get('z', []))\n        transform_styles = GetTransformStyles(to_x, to_y, to_scale_x, to_scale_y, to_rotate_z, to_rotate_y, to_color, to_alpha)\n        FlushCommentLine(f, text, common_styles+transform_styles, c[0]+from_time, c[0]+from_time+action_time, styleid)\n        for action in actions:\n            action = dict(action)\n            from_x, from_y = to_x, to_y\n            from_scale_x, from_scale_y = to_scale_x, to_scale_y\n            from_rotate_z, from_rotate_y = to_rotate_z, to_rotate_y\n            from_color, from_alpha = to_color, to_alpha\n            from_time += action_time\n            action_time = float(action.get('l', 0.0))\n            action_styles = []\n            if 'x' in action:\n                to_x = round(GetPosition(int(action['x']), False))\n            if 'y' in action:\n                to_y = round(GetPosition(int(action['y']), True))\n            if 'f' in action:\n                to_scale_x = round(float(action['f'])*100)\n                action_styles.append('\\\\fscx%s' % to_scale_x)\n            if 'g' in action:\n                to_scale_y = round(float(action['g'])*100)\n                action_styles.append('\\\\fscy%s' % to_scale_y)\n            if 'c' in action:\n                to_color = int(action['c'])\n                action_styles.append('\\\\c&H%02X%02X%02X&' % (to_color & 0xff, (to_color >> 8) & 0xff, (to_color >> 16) & 0xff))\n            if 't' in action:\n                to_alpha = float(action['t'])\n                action_styles.append('\\\\alpha&H%02X' % (255-round(to_alpha*255)))\n            if 'd' in action:\n                to_rotate_z = float(action['d'])\n            if 'e' in action:\n                to_rotate_y = float(action['e'])\n            if ('x' in action) or ('y' in action):\n                transform_styles = GetTransformStyles(None, None, from_scale_x, from_scale_y, None, None, from_color, from_alpha)\n                transform_styles.append('\\\\move(%s, %s, %s, %s)' % (from_x, from_y, to_x, to_y))\n                action_styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(to_rotate_y, to_rotate_z, (to_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (to_y-ZoomFactor[2])/(width-ZoomFactor[2]*2)))\n            elif ('d' in action) or ('e' in action):\n                action_styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(to_rotate_y, to_rotate_z, (to_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (to_y-ZoomFactor[2])/(width-ZoomFactor[2]*2)))\n            else:\n                transform_styles = GetTransformStyles(from_x, from_y, from_scale_x, from_scale_y, from_rotate_z, from_rotate_y, from_color, from_alpha)\n            if action_styles:\n                transform_styles.append('\\\\t(%s)' % (''.join(action_styles)))\n            FlushCommentLine(f, text, common_styles+transform_styles, c[0]+from_time, c[0]+from_time+action_time, styleid)\n    except (IndexError, ValueError) as e:\n        logging.warning(_('Invalid comment: %r') % c[3])\n\n\ndef WriteCommentSH5VPositioned(f, c, width, height, styleid):\n\n    def GetTransformStyles(x=None, y=None, fsize=None, rotate_z=None, rotate_y=None, color=None, alpha=None):\n        styles = []\n        if x is not None and y is not None:\n            styles.append('\\\\pos(%s, %s)' % (x, y))\n        if fsize is not None:\n            styles.append('\\\\fs%s' % fsize)\n        if rotate_y is not None and rotate_z is not None:\n            styles.append('\\\\frz%s' % round(rotate_z))\n            styles.append('\\\\fry%s' % round(rotate_y))\n        if color is not None:\n            styles.append('\\\\c&H%02X%02X%02X&' % (color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff))\n            if color == 0x000000:\n                styles.append('\\\\3c&HFFFFFF&')\n        if alpha is not None:\n            alpha = 255-round(alpha*255)\n            styles.append('\\\\alpha&H%02X' % alpha)\n        return styles\n\n    def FlushCommentLine(f, text, styles, start_time, end_time, styleid):\n        if end_time > start_time:\n            f.write('Dialogue: -1,%(start)s,%(end)s,%(styleid)s,,0,0,0,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(start_time), 'end': ConvertTimestamp(end_time), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n\n    try:\n        text = ASSEscape(str(c[3]))\n        to_x = round(float(c[9])*width)\n        to_y = round(float(c[10])*height)\n        to_rotate_z = -int(c[14])\n        to_rotate_y = -int(c[15])\n        to_color = c[5]\n        to_alpha = float(c[12])\n        #Note: Alpha transition hasn't been worked out yet.\n        to_size = round(int(c[6])*math.sqrt(width*height/307200))\n        #Note: Because sH5V's data is the absolute size of font,temporarily solve by it at present.[*math.sqrt(width/640*height/480)]\n        #But it seems to be working fine...\n        from_time = float(c[0])\n        action_time = float(c[11])/1000\n        transform_styles = GetTransformStyles(to_x, to_y, to_size, to_rotate_z, to_rotate_y, to_color, to_alpha)\n        FlushCommentLine(f, text, transform_styles, from_time, from_time+action_time, styleid)\n    except (IndexError, ValueError) as e:\n        logging.warning(_('Invalid comment: %r') % c[3])\n\n\n# Result: (f, dx, dy)\n# To convert: NewX = f*x+dx, NewY = f*y+dy\ndef GetZoomFactor(SourceSize, TargetSize):\n    try:\n        if (SourceSize, TargetSize) == GetZoomFactor.Cached_Size:\n            return GetZoomFactor.Cached_Result\n    except AttributeError:\n        pass\n    GetZoomFactor.Cached_Size = (SourceSize, TargetSize)\n    try:\n        SourceAspect = SourceSize[0]/SourceSize[1]\n        TargetAspect = TargetSize[0]/TargetSize[1]\n        if TargetAspect < SourceAspect:  # narrower\n            ScaleFactor = TargetSize[0]/SourceSize[0]\n            GetZoomFactor.Cached_Result = (ScaleFactor, 0, (TargetSize[1]-TargetSize[0]/SourceAspect)/2)\n        elif TargetAspect > SourceAspect:  # wider\n            ScaleFactor = TargetSize[1]/SourceSize[1]\n            GetZoomFactor.Cached_Result = (ScaleFactor, (TargetSize[0]-TargetSize[1]*SourceAspect)/2, 0)\n        else:\n            GetZoomFactor.Cached_Result = (TargetSize[0]/SourceSize[0], 0, 0)\n        return GetZoomFactor.Cached_Result\n    except ZeroDivisionError:\n        GetZoomFactor.Cached_Result = (1, 0, 0)\n        return GetZoomFactor.Cached_Result\n\n\n# Calculation is based on https://github.com/jabbany/CommentCoreLibrary/issues/5#issuecomment-40087282\n#                     and https://github.com/m13253/danmaku2ass/issues/7#issuecomment-41489422\n# Input: X relative horizonal coordinate: 0 for left edge, 1 for right edge.\n#        Y relative vertical coordinate: 0 for top edge, 1 for bottom edge.\n# FOV = 1.0/math.tan(100*math.pi/360.0)\n# Result: (rotX, rotY, rotZ, shearX, shearY)\ndef ConvertFlashRotation(rotY, rotZ, X, Y, FOV=math.tan(2*math.pi/9.0)):\n    def WrapAngle(deg):\n        return 180-((180-deg)%360)\n    def CalcPerspectiveCorrection(alpha, X, FOV=FOV):\n        alpha = WrapAngle(alpha)\n        if FOV is None:\n            return alpha\n        if 0 <= alpha <= 180:\n            costheta = (FOV*math.cos(alpha*math.pi/180.0)-X*math.sin(alpha*math.pi/180.0))/(FOV+max(2, abs(X)+1)*math.sin(alpha*math.pi/180.0))\n            try:\n                if costheta > 1:\n                    costheta = 1\n                    raise ValueError\n                elif costheta < -1:\n                    costheta = -1\n                    raise ValueError\n            except ValueError:\n                logging.error('Clipped rotation angle: (alpha=%s, X=%s), it is a bug!' % (alpha, X))\n            theta = math.acos(costheta)*180/math.pi\n        else:\n            costheta = (FOV*math.cos(alpha*math.pi/180.0)-X*math.sin(alpha*math.pi/180.0))/(FOV-max(2, abs(X)+1)*math.sin(alpha*math.pi/180.0))\n            try:\n                if costheta > 1:\n                    costheta = 1\n                    raise ValueError\n                elif costheta < -1:\n                    costheta = -1\n                    raise ValueError\n            except ValueError:\n                logging.error('Clipped rotation angle: (alpha=%s, X=%s), it is a bug!' % (alpha, X))\n            theta = -math.acos(costheta)*180/math.pi\n        return WrapAngle(theta)\n    X = 2*X-1\n    Y = 2*Y-1\n    rotY = WrapAngle(rotY)\n    rotZ = WrapAngle(rotZ)\n    if rotY == 0 or rotZ == 0:\n        outX = 0\n        outY = -rotY  # Positive value means clockwise in Flash\n        outZ = -rotZ\n    else:\n        rotY = rotY*math.pi/180.0\n        rotZ = rotZ*math.pi/180.0\n        outY = math.atan2(-math.sin(rotY)*math.cos(rotZ), math.cos(rotY))*180/math.pi\n        outZ = math.atan2(-math.cos(rotY)*math.sin(rotZ), math.cos(rotZ))*180/math.pi\n        outX = math.asin(math.sin(rotY)*math.sin(rotZ))*180/math.pi\n    if FOV is not None:\n        #outX = CalcPerspectiveCorrection(outX, -Y, FOV*0.75)\n        outY = CalcPerspectiveCorrection(outY, X, FOV)\n    return (WrapAngle(round(outX)), WrapAngle(round(outY)), WrapAngle(round(outZ)), 0, round(-0.75*Y*math.sin(outY*math.pi/180.0), 3))\n\n\ndef ProcessComments(comments, f, width, height, bottomReserved, fontface, fontsize, alpha, lifetime, reduced, progress_callback):\n    styleid = 'Danmaku2ASS_%04x' % random.randint(0, 0xffff)\n    WriteASSHead(f, width, height, fontface, fontsize, alpha, styleid)\n    rows = [[None]*(height-bottomReserved+1) for i in range(4)]\n    for idx, i in enumerate(comments):\n        if progress_callback and idx % 1000 == 0:\n            progress_callback(idx, len(comments))\n        if isinstance(i[4], int):\n            row = 0\n            rowmax = height-bottomReserved-i[7]\n            while row <= rowmax:\n                freerows = TestFreeRows(rows, i, row, width, height, bottomReserved, lifetime)\n                if freerows >= i[7]:\n                    MarkCommentRow(rows, i, row)\n                    WriteComment(f, i, row, width, height, bottomReserved, fontsize, lifetime, styleid)\n                    break\n                else:\n                    row += freerows or 1\n            else:\n                if not reduced:\n                    row = FindAlternativeRow(rows, i, height, bottomReserved)\n                    MarkCommentRow(rows, i, row)\n                    WriteComment(f, i, row, width, height, bottomReserved, fontsize, lifetime, styleid)\n        elif i[4] == 'bilipos':\n            WriteCommentBilibiliPositioned(f, i, width, height, styleid)\n        elif i[4] == 'acfunpos':\n            WriteCommentAcfunPositioned(f, i, width, height, styleid)\n        elif i[4] == 'sH5Vpos':\n            WriteCommentSH5VPositioned(f, i, width, height, styleid)\n        else:\n            logging.warning(_('Invalid comment: %r') % i[3])\n    if progress_callback:\n        progress_callback(len(comments), len(comments))\n\n\ndef TestFreeRows(rows, c, row, width, height, bottomReserved, lifetime):\n    res = 0\n    rowmax = height-bottomReserved\n    targetRow = None\n    if c[4] in (1, 2):\n        while row < rowmax and res < c[7]:\n            if targetRow != rows[c[4]][row]:\n                targetRow = rows[c[4]][row]\n                if targetRow and targetRow[0]+lifetime > c[0]:\n                    break\n            row += 1\n            res += 1\n    else:\n        try:\n            thresholdTime = c[0]-lifetime*(1-width/(c[8]+width))\n        except ZeroDivisionError:\n            thresholdTime = c[0]-lifetime\n        while row < rowmax and res < c[7]:\n            if targetRow != rows[c[4]][row]:\n                targetRow = rows[c[4]][row]\n                try:\n                    if targetRow and (targetRow[0] > thresholdTime or targetRow[0]+targetRow[8]*lifetime/(targetRow[8]+width) > c[0]):\n                        break\n                except ZeroDivisionError:\n                    pass\n            row += 1\n            res += 1\n    return res\n\n\ndef FindAlternativeRow(rows, c, height, bottomReserved):\n    res = 0\n    for row in range(height-bottomReserved-math.ceil(c[7])):\n        if not rows[c[4]][row]:\n            return row\n        elif rows[c[4]][row][0] < rows[c[4]][res][0]:\n            res = row\n    return res\n\n\ndef MarkCommentRow(rows, c, row):\n    try:\n        for i in range(row, row+math.ceil(c[7])):\n            rows[c[4]][i] = c\n    except IndexError:\n        pass\n\n\ndef WriteASSHead(f, width, height, fontface, fontsize, alpha, styleid):\n    f.write(\n'''\n[Script Info]\n; Script generated by Danmaku2ASS\n; https://github.com/m13253/danmaku2ass\nScript Updated By: Danmaku2ASS (https://github.com/m13253/danmaku2ass)\nScriptType: v4.00+\nWrapStyle: 2\nCollisions: Normal\nPlayResX: %(width)s\nPlayResY: %(height)s\nScaledBorderAndShadow: yes\n[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\nStyle: %(styleid)s, %(fontface)s, %(fontsize)s, &H%(alpha)02XFFFFFF, &H%(alpha)02XFFFFFF, &H%(alpha)02X000000, &H%(alpha)02X000000, 0, 0, 0, 0, 100, 100, 0.00, 0.00, 1, %(outline)s, 0, 7, 0, 0, 0, 0\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n''' % {'width': width, 'height': height, 'fontface': fontface, 'fontsize': round(fontsize), 'alpha': 255-round(alpha*255), 'outline': round(fontsize/25), 'styleid': styleid}\n    )\n\n\ndef WriteComment(f, c, row, width, height, bottomReserved, fontsize, lifetime, styleid):\n    text = ASSEscape(c[3])\n    styles = []\n    if c[4] == 1:\n        styles.append('\\\\an8\\\\pos(%(halfwidth)s, %(row)s)' % {'halfwidth': round(width/2), 'row': row})\n    elif c[4] == 2:\n        styles.append('\\\\an2\\\\pos(%(halfwidth)s, %(row)s)' % {'halfwidth': round(width/2), 'row': ConvertType2(row, height, bottomReserved)})\n    elif c[4] == 3:\n        styles.append('\\\\move(%(neglen)s, %(row)s, %(width)s, %(row)s)' % {'width': width, 'row': row, 'neglen': -math.ceil(c[8])})\n    else:\n        styles.append('\\\\move(%(width)s, %(row)s, %(neglen)s, %(row)s)' % {'width': width, 'row': row, 'neglen': -math.ceil(c[8])})\n    if not (-1 < c[6]-fontsize < 1):\n        styles.append('\\\\fs%s' % round(c[6]))\n    if c[5] != 0xffffff:\n        styles.append('\\\\c&H%02X%02X%02X&' % (c[5] & 0xff, (c[5] >> 8) & 0xff, (c[5] >> 16) & 0xff))\n        if c[5] == 0x000000:\n            styles.append('\\\\3c&HFFFFFF&')\n    f.write('Dialogue: 2,%(start)s,%(end)s,%(styleid)s,,0000,0000,0000,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(c[0]), 'end': ConvertTimestamp(c[0]+lifetime), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n\n\ndef ASSEscape(s):\n    return '\\\\N'.join((i or ' ' for i in str(s).replace('\\\\', '\\\\\\\\').replace('{', '\\\\{').replace('}', '\\\\}').split('\\n')))\n\n\ndef CalculateLength(s):\n    return max(map(len, s.split('\\n')))  # May not be accurate\n\n\ndef ConvertTimestamp(timestamp):\n    timestamp = round(timestamp*100.0)\n    hour, minute = divmod(timestamp, 360000)\n    minute, second = divmod(minute, 6000)\n    second, centsecond = divmod(second, 100)\n    return '%d:%02d:%02d.%02d' % (int(hour), int(minute), int(second), int(centsecond))\n\n\ndef ConvertType2(row, height, bottomReserved):\n    return height-bottomReserved-row\n\n\ndef ConvertToFile(filename_or_file, *args, **kwargs):\n    if isinstance(filename_or_file, bytes):\n        filename_or_file = str(bytes(filename_or_file).decode('utf-8', 'replace'))\n    if isinstance(filename_or_file, str):\n        return open(filename_or_file, *args, **kwargs)\n    else:\n        return filename_or_file\n\n\ndef FilterBadChars(f):\n    s = f.read()\n    s = re.sub('[\\\\x00-\\\\x08\\\\x0b\\\\x0c\\\\x0e-\\\\x1f]', '\\ufffd', s)\n    return io.StringIO(s)\n\n\nclass safe_list(list):\n    def get(self, index, default=None):\n        try:\n            return self[index]\n        except IndexError:\n            return default\n\n\ndef export(func):\n    global __all__\n    try:\n        __all__.append(func.__name__)\n    except NameError:\n        __all__ = [func.__name__]\n    return func\n\n\n@export\ndef Danmaku2ASS(input_files, output_file, stage_width, stage_height, reserve_blank=0, font_face=_('(FONT) sans-serif')[7:], font_size=25.0, text_opacity=1.0, comment_duration=5.0, is_reduce_comments=False, progress_callback=None):\n    fo = None\n    comments = ReadComments(input_files, font_size)\n    try:\n        if output_file:\n            fo = ConvertToFile(output_file, 'w', encoding='utf-8-sig', errors='replace', newline='\\r\\n')\n        else:\n            fo = sys.stdout\n        ProcessComments(comments, fo, stage_width, stage_height, reserve_blank, font_face, font_size, text_opacity, comment_duration, is_reduce_comments, progress_callback)\n    finally:\n        if output_file and fo != output_file:\n            fo.close()\n\n\n@export\ndef ReadComments(input_files, font_size=25.0, progress_callback=None):\n    if isinstance(input_files, bytes):\n        input_files = str(bytes(input_files).decode('utf-8', 'replace'))\n    if isinstance(input_files, str):\n        input_files = [input_files]\n    else:\n        input_files = list(input_files)\n    comments = []\n    for idx, i in enumerate(input_files):\n        if progress_callback:\n            progress_callback(idx, len(input_files))\n        with ConvertToFile(i, 'r', encoding='utf-8', errors='replace') as f:\n            CommentProcessor = GetCommentProcessor(f)\n            if not CommentProcessor:\n                raise ValueError(_('Unknown comment file format: %s') % i)\n            comments.extend(CommentProcessor(FilterBadChars(f), font_size))\n    if progress_callback:\n        progress_callback(len(input_files), len(input_files))\n    comments.sort()\n    return comments\n\n\n@export\ndef GetCommentProcessor(input_file):\n    return CommentFormatMap[ProbeCommentFormat(input_file)]\n\n\ndef main():\n    if len(sys.argv) == 1:\n        sys.argv.append('--help')\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-o', '--output', metavar=_('OUTPUT'), help=_('Output file'))\n    parser.add_argument('-s', '--size', metavar=_('WIDTHxHEIGHT'), required=True, help=_('Stage size in pixels'))\n    parser.add_argument('-fn', '--font', metavar=_('FONT'), help=_('Specify font face [default: %s]') % _('(FONT) sans-serif')[7:], default=_('(FONT) sans-serif')[7:])\n    parser.add_argument('-fs', '--fontsize', metavar=_('SIZE'), help=(_('Default font size [default: %s]') % 25), type=float, default=25.0)\n    parser.add_argument('-a', '--alpha', metavar=_('ALPHA'), help=_('Text opacity'), type=float, default=1.0)\n    parser.add_argument('-l', '--lifetime', metavar=_('SECONDS'), help=_('Duration of comment display [default: %s]') % 5, type=float, default=5.0)\n    parser.add_argument('-p', '--protect', metavar=_('HEIGHT'), help=_('Reserve blank on the bottom of the stage'), type=int, default=0)\n    parser.add_argument('-r', '--reduce', action='store_true', help=_('Reduce the amount of comments if stage is full'))\n    parser.add_argument('file', metavar=_('FILE'), nargs='+', help=_('Comment file to be processed'))\n    args = parser.parse_args()\n    try:\n        width, height = str(args.size).split('x', 1)\n        width = int(width)\n        height = int(height)\n    except ValueError:\n        raise ValueError(_('Invalid stage size: %r') % args.size)\n    Danmaku2ASS(args.file, args.output, width, height, args.protect, args.font, args.fontsize, args.alpha, args.lifetime, args.reduce)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "2020/dmzj/cartoon.py",
    "content": "import requests\nimport os\nimport re\nfrom bs4 import BeautifulSoup\nfrom contextlib import closing\nfrom tqdm import tqdm\nimport time\n\n\"\"\"\n    Author:\n        Jack Cui\n    Wechat:\n        https://mp.weixin.qq.com/s/OCWwRVDFNslIuKyiCVUoTA\n\"\"\"\n\n# 创建保存目录\nsave_dir = '妖神记'\nif save_dir not in os.listdir('./'):\n    os.mkdir(save_dir)\n    \ntarget_url = \"https://www.dmzj.com/info/yaoshenji.html\"\n\n# 获取动漫章节链接和章节名\nr = requests.get(url = target_url)\nbs = BeautifulSoup(r.text, 'lxml')\nlist_con_li = bs.find('ul', class_=\"list_con_li\")\ncartoon_list = list_con_li.find_all('a')\nchapter_names = []\nchapter_urls = []\nfor cartoon in cartoon_list:\n    href = cartoon.get('href')\n    name = cartoon.text\n    chapter_names.insert(0, name)\n    chapter_urls.insert(0, href)\n\n# 下载漫画 \nfor i, url in enumerate(tqdm(chapter_urls)):\n    download_header = {\n        'Referer': url\n    }\n    name = chapter_names[i]\n    # 去掉.\n    while '.' in name:\n        name = name.replace('.', '')\n    chapter_save_dir = os.path.join(save_dir, name)\n    if name not in os.listdir(save_dir):\n        os.mkdir(chapter_save_dir)\n    r = requests.get(url = url)\n    html = BeautifulSoup(r.text, 'lxml')\n    script_info = html.script\n    pics = re.findall('\\d{13,14}', str(script_info))\n    for j, pic in enumerate(pics):\n        if len(pic) == 13:\n            pics[j] = pic + '0'\n    pics = sorted(pics, key=lambda x:int(x))\n    chapterpic_hou = re.findall('\\|(\\d{5})\\|', str(script_info))[0]\n    chapterpic_qian = re.findall('\\|(\\d{4})\\|', str(script_info))[0]\n    for idx, pic in enumerate(pics):\n        if pic[-1] == '0':\n            url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic[:-1] + '.jpg'\n        else:\n            url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic + '.jpg'\n        pic_name = '%03d.jpg' % (idx + 1)\n        pic_save_path = os.path.join(chapter_save_dir, pic_name)\n        with closing(requests.get(url, headers = download_header, stream = True)) as response:  \n            chunk_size = 1024  \n            content_size = int(response.headers['content-length'])  \n            if response.status_code == 200:\n                with open(pic_save_path, \"wb\") as file:  \n                    for data in response.iter_content(chunk_size=chunk_size):  \n                        file.write(data)  \n            else:\n                print('链接异常')\n    time.sleep(10)"
  },
  {
    "path": "2020/taobao/taobao_login.py",
    "content": "from selenium import webdriver\nimport logging\nimport time\nfrom selenium.common.exceptions import NoSuchElementException, WebDriverException\nfrom retrying import retry\nfrom selenium.webdriver import ActionChains\n\nimport pyautogui\npyautogui.PAUSE = 0.5 \n\nlogging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')\nlogger = logging.getLogger(__name__)\n\n\"\"\"\n微信公众号 JackCui-AI\n更多精彩教程、源码尽在微信公众号\n\"\"\"\n\nclass taobao():\n    def __init__(self):\n        self.browser = webdriver.Chrome(\"path\\to\\your\\chromedriver.exe\")\n        # 最大化窗口\n        self.browser.maximize_window()\n        self.browser.implicitly_wait(5)\n        self.domain = 'http://www.taobao.com'\n        self.action_chains = ActionChains(self.browser)\n\n    def login(self, username, password):\n        while True:\n            self.browser.get(self.domain)\n            time.sleep(1)\n            \n            #会xpath可以简化这几步\n            #self.browser.find_element_by_class_name('h').click()\n            #self.browser.find_element_by_id('fm-login-id').send_keys(username)\n            #self.browser.find_element_by_id('fm-login-password').send_keys(password)\n            self.browser.find_element_by_xpath('//*[@id=\"J_SiteNavLogin\"]/div[1]/div[1]/a[1]').click()\n            self.browser.find_element_by_xpath('//*[@id=\"fm-login-id\"]').send_keys(username)\n            self.browser.find_element_by_xpath('//*[@id=\"fm-login-password\"]').send_keys(password)\n            time.sleep(1)\n\n            try:\n                # 出现验证码，滑动验证\n                slider = self.browser.find_element_by_xpath(\"//span[contains(@class, 'btn_slide')]\")\n                if slider.is_displayed():\n                    # 拖拽滑块\n                    self.action_chains.drag_and_drop_by_offset(slider, 258, 0).perform()\n                    time.sleep(0.5)\n                    # 释放滑块，相当于点击拖拽之后的释放鼠标\n                    self.action_chains.release().perform()\n            except (NoSuchElementException, WebDriverException):\n                logger.info('未出现登录验证码')\n            \n            # 会xpath可以简化点击登陆按钮，但都无法登录，需要使用 pyautogui 完成点击事件\n            #self.browser.find_element_by_class_name('password-login').click()\n            #self.browser.find_element_by_xpath('//*[@id=\"login-form\"]/div[4]/button').click()\n            # 图片地址\n            coords = pyautogui.locateOnScreen('1.png')\n            x, y = pyautogui.center(coords)\n            pyautogui.leftClick(x, y)\n            \n            nickname = self.get_nickname()\n            if nickname:\n                logger.info('登录成功，呢称为:' + nickname)\n                break\n            logger.debug('登录出错，5s后继续登录')\n            time.sleep(5)\n\n    def get_nickname(self):\n        self.browser.get(self.domain)\n        time.sleep(0.5)\n        try:\n            return self.browser.find_element_by_class_name('site-nav-user').text\n        except NoSuchElementException:\n            return ''\n            \n    def clear_cart(self):\n        cart = self.browser.find_element_by_xpath('//*[@id=\"J_MiniCart\"]')\n        if cart.is_displayed():\n            cart.click()\n        select = self.browser.find_element_by_xpath('//*[@id=\"J_SelectAll1\"]/div/label')\n        if select.is_displayed():\n            select.click()\n        time.sleep(0.5)\n        go = self.browser.find_element_by_xpath('//*[@id=\"J_Go\"]')\n        if go.is_displayed():\n            go.click()\n        submit = self.browser.find_element_by_xpath('//*[@id=\"submitOrderPC_1\"]/div/a[2]')\n        if submit.is_displayed():\n            submit.click()\n\n\nif __name__ == '__main__':\n    # 填入自己的用户名，密码\n    username = 'username'\n    password = 'password'\n    tb = taobao()\n    tb.login(username, password)\n    #tb.clear_cart()\n"
  },
  {
    "path": "2020/xbqg/xbqg_spider.py",
    "content": "import requests\nimport time\nfrom tqdm import tqdm\nfrom bs4 import BeautifulSoup\n\n\"\"\"\n    Author:\n        Jack Cui\n    Wechat:\n        https://mp.weixin.qq.com/s/OCWwRVDFNslIuKyiCVUoTA\n\"\"\"\n\ndef get_content(target):\n    req = requests.get(url = target)\n    req.encoding = 'utf-8'\n    html = req.text\n    bf = BeautifulSoup(html, 'lxml')\n    texts = bf.find('div', id='content')\n    content = texts.text.strip().split('\\xa0'*4)\n    return content\n\nif __name__ == '__main__':\n    server = 'https://www.xsbiquge.com'\n    book_name = '诡秘之主.txt'\n    target = 'https://www.xsbiquge.com/15_15338/'\n    req = requests.get(url = target)\n    req.encoding = 'utf-8'\n    html = req.text\n    chapter_bs = BeautifulSoup(html, 'lxml')\n    chapters = chapter_bs.find('div', id='list')\n    chapters = chapters.find_all('a')\n    for chapter in tqdm(chapters):\n        chapter_name = chapter.string\n        url = server + chapter.get('href')\n        content = get_content(url)\n        with open(book_name, 'a', encoding='utf-8') as f:\n            f.write(chapter_name)\n            f.write('\\n')\n            f.write('\\n'.join(content))\n            f.write('\\n')"
  },
  {
    "path": "2020/zycjw/video_download.py",
    "content": "import os\nimport ffmpy3\nimport requests\nfrom bs4 import BeautifulSoup\nfrom multiprocessing.dummy import Pool as ThreadPool\n\nsearch_keyword = '越狱第一季'\nsearch_url = 'http://www.jisudhw.com/index.php'\nserach_params = {\n    'm': 'vod-search'\n}\nserach_headers = {\n    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36',\n    'Referer': 'http://www.jisudhw.com/',\n    'Origin': 'http://www.jisudhw.com',\n    'Host': 'www.jisudhw.com'\n}\nserach_datas = {\n    'wd': search_keyword,\n    'submit': 'search'\n}\n\n\nvideo_dir = ''\n    \nr = requests.post(url=search_url, params=serach_params, headers=serach_headers, data=serach_datas)\nr.encoding = 'utf-8'\nserver = 'http://www.jisudhw.com'\nsearch_html = BeautifulSoup(r.text, 'lxml')\nsearch_spans = search_html.find_all('span', class_='xing_vb4')\nfor span in search_spans:\n    url = server + span.a.get('href')\n    name = span.a.string\n    print(name)\n    print(url)\n    video_dir = name\n    if name not in os.listdir('./'):\n        os.mkdir(name)\n        \n    detail_url = url\n    r = requests.get(url = detail_url)\n    r.encoding = 'utf-8'\n    detail_bf = BeautifulSoup(r.text, 'lxml')\n    num = 1\n    serach_res = {}\n    for each_url in detail_bf.find_all('input'):\n        if 'm3u8' in each_url.get('value'):\n            url = each_url.get('value')\n            if url not in serach_res.keys():\n                serach_res[url] = num\n            print('第%03d集:' % num)\n            print(url)\n            num += 1\n\ndef downVideo(url):\n    num = serach_res[url]\n    name = os.path.join(video_dir, '第%03d集.mp4' % num)\n    ffmpy3.FFmpeg(inputs={url: None}, outputs={name:None}).run()\n            \n# 开8个线程池\npool = ThreadPool(8)\nresults = pool.map(downVideo, serach_res.keys())\npool.close()\npool.join()"
  },
  {
    "path": "Netease/Netease.py",
    "content": "# -*- coding:utf-8 -*-\r\nimport requests, hashlib, sys, click, re, base64, binascii, json, os\r\nfrom Crypto.Cipher import AES\r\nfrom http import cookiejar\r\n\r\n\"\"\"\r\nWebsite:http://cuijiahua.com\r\nAuthor:Jack Cui\r\nRefer:https://github.com/darknessomi/musicbox\r\n\"\"\"\r\n\r\nclass Encrypyed():\r\n\t\"\"\"\r\n\t解密算法\r\n\t\"\"\"\r\n\tdef __init__(self):\r\n\t\tself.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'\r\n\t\tself.nonce = '0CoJUm6Qyw8W8jud'\r\n\t\tself.pub_key = '010001'\r\n\r\n\t# 登录加密算法, 基于https://github.com/stkevintan/nw_musicbox脚本实现\r\n\tdef encrypted_request(self, text):\r\n\t\ttext = json.dumps(text)\r\n\t\tsec_key = self.create_secret_key(16)\r\n\t\tenc_text = self.aes_encrypt(self.aes_encrypt(text, self.nonce), sec_key.decode('utf-8'))\r\n\t\tenc_sec_key = self.rsa_encrpt(sec_key, self.pub_key, self.modulus)\r\n\t\tdata = {'params': enc_text, 'encSecKey': enc_sec_key}\r\n\t\treturn data\r\n\r\n\tdef aes_encrypt(self, text, secKey):\r\n\t\tpad = 16 - len(text) % 16\r\n\t\ttext = text + chr(pad) * pad\r\n\t\tencryptor = AES.new(secKey.encode('utf-8'), AES.MODE_CBC, b'0102030405060708')\r\n\t\tciphertext = encryptor.encrypt(text.encode('utf-8'))\r\n\t\tciphertext = base64.b64encode(ciphertext).decode('utf-8')\r\n\t\treturn ciphertext\r\n\r\n\tdef rsa_encrpt(self, text, pubKey, modulus):\r\n\t\ttext = text[::-1]\r\n\t\trs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16), int(modulus, 16))\r\n\t\treturn format(rs, 'x').zfill(256)\r\n\r\n\tdef create_secret_key(self, size):\r\n\t\treturn binascii.hexlify(os.urandom(size))[:16]\r\n\r\n\r\nclass Song():\r\n\t\"\"\"\r\n\t歌曲对象，用于存储歌曲的信息\r\n\t\"\"\"\r\n\tdef __init__(self, song_id, song_name, song_num, song_url=None):\r\n\t\tself.song_id = song_id\r\n\t\tself.song_name = song_name\r\n\t\tself.song_num = song_num\r\n\t\tself.song_url = '' if song_url is None else song_url\r\n\r\nclass Crawler():\r\n\t\"\"\"\r\n\t网易云爬取API\r\n\t\"\"\"\r\n\tdef __init__(self, timeout=60, cookie_path='.'):\r\n\t\tself.headers = {\r\n\t\t\t'Accept': '*/*',\r\n\t\t\t'Accept-Encoding': 'gzip,deflate,sdch',\r\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',\r\n\t\t\t'Connection': 'keep-alive',\r\n\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\r\n\t\t\t'Host': 'music.163.com',\r\n\t\t\t'Referer': 'http://music.163.com/search/',\r\n\t\t\t'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'\r\n\t\t}\r\n\t\tself.session = requests.Session()\r\n\t\tself.session.headers.update(self.headers)\r\n\t\tself.session.cookies = cookiejar.LWPCookieJar(cookie_path)\r\n\t\tself.download_session = requests.Session()\r\n\t\tself.timeout = timeout\r\n\t\tself.ep = Encrypyed()\r\n\r\n\tdef post_request(self, url, params):\r\n\t\t\"\"\"\r\n\t\tPost请求\r\n\t\t:return: 字典\r\n\t\t\"\"\"\r\n\r\n\t\tdata = self.ep.encrypted_request(params)\r\n\t\tresp = self.session.post(url, data=data, timeout=self.timeout)\r\n\t\tresult = resp.json()\r\n\t\tif result['code'] != 200:\r\n\t\t\tclick.echo('post_request error')\r\n\t\telse:\r\n\t\t    return result\r\n\r\n\tdef search(self, search_content, search_type, limit=9):\r\n\t\t\"\"\"\r\n\t\t搜索API\r\n\t\t:params search_content: 搜索内容\r\n\t\t:params search_type: 搜索类型\r\n\t\t:params limit: 返回结果数量\r\n\t\t:return: 字典.\r\n\t\t\"\"\"\r\n\r\n\t\turl = 'http://music.163.com/weapi/cloudsearch/get/web?csrf_token='\r\n\t\tparams = {'s': search_content, 'type': search_type, 'offset': 0, 'sub': 'false', 'limit': limit}\r\n\t\tresult = self.post_request(url, params)\r\n\t\treturn result\r\n\r\n\tdef search_song(self, song_name, song_num, quiet=True, limit=9):\r\n\t\t\"\"\"\r\n\t\t根据音乐名搜索\r\n\t\t:params song_name: 音乐名\r\n\t\t:params song_num: 下载的歌曲数\r\n\t\t:params quiet: 自动选择匹配最优结果\r\n\t\t:params limit: 返回结果数量\r\n\t\t:return: Song独享\r\n\t\t\"\"\"\r\n\r\n\t\tresult = self.search(song_name, search_type=1, limit=limit)\r\n\r\n\t\tif result['result']['songCount'] <= 0:\r\n\t\t\tclick.echo('Song {} not existed.'.format(song_name))\r\n\t\telse:\r\n\t\t\tsongs = result['result']['songs']\r\n\t\t\tif quiet:\r\n\t\t\t\tsong_id, song_name = songs[0]['id'], songs[0]['name']\r\n\t\t\t\tsong = Song(song_id=song_id, song_name=song_name, song_num=song_num)\r\n\t\t\t\treturn song\r\n\r\n\tdef get_song_url(self, song_id, bit_rate=320000):\r\n\t\t\"\"\"\r\n\t\t获得歌曲的下载地址\r\n\t\t:params song_id: 音乐ID<int>.\r\n\t\t:params bit_rate: {'MD 128k': 128000, 'HD 320k': 320000}\r\n\t\t:return: 歌曲下载地址\r\n\t\t\"\"\"\r\n\r\n\t\turl = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token='\r\n\t\tcsrf = ''\r\n\t\tparams = {'ids': [song_id], 'br': bit_rate, 'csrf_token': csrf}\r\n\t\tresult = self.post_request(url, params)\r\n\t\t# 歌曲下载地址\r\n\t\tsong_url = result['data'][0]['url']\r\n\r\n\t\t# 歌曲不存在\r\n\t\tif song_url is None:\r\n\t\t\tclick.echo('Song {} is not available due to copyright issue.'.format(song_id))\r\n\t\telse:\r\n\t\t\treturn song_url\r\n\r\n\tdef get_song_by_url(self, song_url, song_name, song_num, folder):\r\n\t\t\"\"\"\r\n\t\t下载歌曲到本地\r\n\t\t:params song_url: 歌曲下载地址\r\n\t\t:params song_name: 歌曲名字\r\n\t\t:params song_num: 下载的歌曲数\r\n\t\t:params folder: 保存路径\r\n\t\t\"\"\"\r\n\t\tif not os.path.exists(folder):\r\n\t\t\tos.makedirs(folder)\r\n\t\tfpath = os.path.join(folder, str(song_num) + '_' + song_name + '.mp3')\r\n\t\tif sys.platform == 'win32' or sys.platform == 'cygwin':\r\n\t\t\tvalid_name = re.sub(r'[<>:\"/\\\\|?*]', '', song_name)\r\n\t\t\tif valid_name != song_name:\r\n\t\t\t\tclick.echo('{} will be saved as: {}.mp3'.format(song_name, valid_name))\r\n\t\t\t\tfpath = os.path.join(folder, str(song_num) + '_' + valid_name + '.mp3')\r\n\t\t\r\n\t\tif not os.path.exists(fpath):\r\n\t\t\tresp = self.download_session.get(song_url, timeout=self.timeout, stream=True)\r\n\t\t\tlength = int(resp.headers.get('content-length'))\r\n\t\t\tlabel = 'Downloading {} {}kb'.format(song_name, int(length/1024))\r\n\r\n\t\t\twith click.progressbar(length=length, label=label) as progressbar:\r\n\t\t\t\twith open(fpath, 'wb') as song_file:\r\n\t\t\t\t\tfor chunk in resp.iter_content(chunk_size=1024):\r\n\t\t\t\t\t\tif chunk:\r\n\t\t\t\t\t\t\tsong_file.write(chunk)\r\n\t\t\t\t\t\t\tprogressbar.update(1024)\r\n\r\n\r\nclass Netease():\r\n\t\"\"\"\r\n\t网易云音乐下载\r\n\t\"\"\"\r\n\tdef __init__(self, timeout, folder, quiet, cookie_path):\r\n\t\tself.crawler = Crawler(timeout, cookie_path)\r\n\t\tself.folder = '.' if folder is None else folder\r\n\t\tself.quiet = quiet\r\n\r\n\tdef download_song_by_search(self, song_name, song_num):\r\n\t\t\"\"\"\r\n\t\t根据歌曲名进行搜索\r\n\t\t:params song_name: 歌曲名字\r\n\t\t:params song_num: 下载的歌曲数\r\n\t\t\"\"\"\r\n\r\n\t\ttry:\r\n\t\t\tsong = self.crawler.search_song(song_name, song_num, self.quiet)\r\n\t\texcept:\r\n\t\t\tclick.echo('download_song_by_serach error')\r\n\t\t# 如果找到了音乐, 则下载\r\n\t\tif song != None:\r\n\t\t\tself.download_song_by_id(song.song_id, song.song_name, song.song_num, self.folder)\r\n\r\n\tdef download_song_by_id(self, song_id, song_name, song_num, folder='.'):\r\n\t\t\"\"\"\r\n\t\t通过歌曲的ID下载\r\n\t\t:params song_id: 歌曲ID\r\n\t\t:params song_name: 歌曲名\r\n\t\t:params song_num: 下载的歌曲数\r\n\t\t:params folder: 保存地址\r\n\t\t\"\"\"\r\n\t\ttry:\r\n\t\t\turl = self.crawler.get_song_url(song_id)\r\n\t\t\t# 去掉非法字符\r\n\t\t\tsong_name = song_name.replace('/', '')\r\n\t\t\tsong_name = song_name.replace('.', '')\r\n\t\t\tself.crawler.get_song_by_url(url, song_name, song_num, folder)\r\n\r\n\t\texcept:\r\n\t\t\tclick.echo('download_song_by_id error')\r\n\r\n\r\nif __name__ == '__main__':\r\n\ttimeout = 60\r\n\toutput = 'Musics'\r\n\tquiet = True\r\n\tcookie_path = 'Cookie'\r\n\tnetease = Netease(timeout, output, quiet, cookie_path)\r\n\tmusic_list_name = 'music_list.txt'\r\n\t# 如果music列表存在, 那么开始下载\r\n\tif os.path.exists(music_list_name):\r\n\t\twith open(music_list_name, 'r') as f:\r\n\t\t\tmusic_list = list(map(lambda x: x.strip(), f.readlines()))\r\n\t\tfor song_num, song_name in enumerate(music_list):\r\n\t\t\tnetease.download_song_by_search(song_name,song_num + 1)\r\n\telse:\r\n\t\tclick.echo('music_list.txt not exist.')"
  },
  {
    "path": "Netease/music_list.txt",
    "content": "風見鶏\n外婆的话【不才】\nWe Don't Talk Anymore\n【电吉他】《青鸟》\n小棋童\n千本桜(古筝版)\n妄为\n借我\n你到底有没有爱过我\n七月上\n"
  },
  {
    "path": "README.md",
    "content": "# 注：2020年最新连载教程请移步：[Python Spider 2020](https://github.com/Jack-Cherish/python-spider/tree/master/2020 \"Python Spider 2020\")\n\n免责声明：\n\n大家请以学习为目的使用本仓库，爬虫违法违规的案件：https://github.com/HiddenStrawberry/Crawler_Illegal_Cases_In_China\n\n本仓库的所有内容仅供学习和参考之用，禁止用于商业用途。任何人或组织不得将本仓库的内容用于非法用途或侵犯他人合法权益。本仓库所涉及的爬虫技术仅用于学习和研究，不得用于对其他平台进行大规模爬虫或其他非法行为。对于因使用本仓库内容而引起的任何法律责任，本仓库不承担任何责任。使用本仓库的内容即表示您同意本免责声明的所有条款和条件。\n\n# Python Spider\n\n原创文章每周最少两篇，**后续最新文章**会在[【公众号】](https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg)首发，视频[【B站】](https://space.bilibili.com/331507846)首发，大家可以加我[【微信】](https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg)进**交流群**，技术交流或提意见都可以，欢迎**Star**！\n\n<p align=\"center\">\n  <a href=\"https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg\" target=\"_blank\"><img src=\"https://img.shields.io/badge/weChat-微信群-blue.svg\" alt=\"微信群\"></a>\n  <a href=\"https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg\" target=\"_blank\"><img src=\"https://img.shields.io/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-Jack%20Cui-lightgrey.svg\" alt=\"公众号\"></a>\n  <a href=\"https://space.bilibili.com/331507846\"><img src=\"https://img.shields.io/badge/bilibili-哔哩哔哩-critical\" alt=\"B站\"></a>\n  <a href=\"https://www.zhihu.com/people/Jack--Cui\" target=\"_blank\"><img src=\"https://img.shields.io/badge/zhihu-知乎-informational\" alt=\"知乎\"></a>\n  <a href=\"https://blog.csdn.net/c406495762\" target=\"_blank\"><img src=\"https://img.shields.io/badge/csdn-CSDN-red.svg\" alt=\"CSDN\"></a>\n  <a href=\"https://www.toutiao.com/c/user/token/MS4wLjABAAAA5gJtmezUJ6vli2hZvnN13iLnzKLpuF8gGHeS0iVlmNs/\" target=\"_blank\"><img src=\"https://img.shields.io/badge/toutiao-%E5%A4%B4%E6%9D%A1-important.svg\" alt=\"头条\"></a>\n  <a href=\"https://juejin.im/user/5ea2ca74e51d4546b50d5f9f\" target=\"_blank\"><img src=\"https://img.shields.io/badge/juejin-掘金-blue.svg\" alt=\"掘金\"></a>\n</p>\n\n## 声明\n\n* 代码、教程**仅限于学习交流，请勿用于任何商业用途！**\n\n## 目录\n\n* [爬虫小工具](#爬虫小工具)\n    * [文件下载小助手](https://github.com/Jack-Cherish/python-spider/blob/master/downloader.py \"悬停显示\")\n* [爬虫实战](#爬虫实战)\n    * [笔趣看小说下载](https://github.com/Jack-Cherish/python-spider/blob/master/biqukan.py \"悬停显示\")\n    * [百度文库免费文章下载助手_rev1](https://github.com/Jack-Cherish/python-spider/blob/master/baiduwenku.py \"悬停显示\")\n    * [百度文库免费文章下载助手_rev2](https://github.com/Jack-Cherish/python-spider/blob/master/baiduwenku_pro_1.py \"悬停显示\")\n    * [《帅啊》网帅哥图片下载](https://github.com/Jack-Cherish/python-spider/blob/master/shuaia.py \"悬停显示\")\n    * [构建代理IP池](https://github.com/Jack-Cherish/python-spider/blob/master/daili.py \"悬停显示\")\n    * [《火影忍者》漫画下载](https://github.com/Jack-Cherish/python-spider/tree/master/cartoon \"悬停显示\")\n    * [财务报表下载小助手](https://github.com/Jack-Cherish/python-spider/blob/master/financical.py \"悬停显示\")\n    * [一小时入门网络爬虫](https://github.com/Jack-Cherish/python-spider/tree/master/one_hour_spider \"悬停显示\")\n    * [抖音App视频下载](https://github.com/Jack-Cherish/python-spider/tree/master/douyin \"悬停显示\")\n    * [GEETEST验证码识别](https://github.com/Jack-Cherish/python-spider/blob/master/geetest.py \"悬停显示\")\n    * [12306抢票小助手](https://github.com/Jack-Cherish/python-spider/blob/master/12306.py \"悬停显示\")\n    * [百万英雄答题辅助系统](https://github.com/Jack-Cherish/python-spider/tree/master/baiwan \"悬停显示\")   \n    * [网易云音乐免费音乐批量下载](https://github.com/Jack-Cherish/python-spider/tree/master/Netease \"悬停显示\")\n    * [B站免费视频和弹幕批量下载](https://github.com/Jack-Cherish/python-spider/tree/master/bilibili \"悬停显示\")\n    * [京东商品晒单图下载](https://github.com/Jack-Cherish/python-spider/tree/master/dingdong \"悬停显示\")\n    * [正方教务管理系统个人信息查询](https://github.com/Jack-Cherish/python-spider/tree/master/zhengfang_system_spider \"悬停显示\")\n* [其它](#其它)\n\n## 爬虫小工具\n\n* downloader.py:文件下载小助手\n\n\t一个可以用于下载图片、视频、文件的小工具，有下载进度显示功能。稍加修改即可添加到自己的爬虫中。\n\t\n\t动态示意图：\n\t\n\t![image](https://raw.githubusercontent.com/Jack-Cherish/Pictures/master/9.gif)\n\n## 爬虫实战\n \n * biqukan.py:《笔趣看》盗版小说网站，爬取小说工具\n\n\t第三方依赖库安装：\n\n\t\tpip3 install beautifulsoup4\n\n\t使用方法：\n\n\t\tpython biqukan.py\n\n * baiduwenku.py: 百度文库word文章爬取\n\t\n\t原理说明：http://blog.csdn.net/c406495762/article/details/72331737\n\t\n\t代码不完善，没有进行打包，不具通用性，纯属娱乐。\n\t\n * shuaia.py: 爬取《帅啊》网，帅哥图片\n\n\t《帅啊》网URL：http://www.shuaia.net/index.html\n\n\t原理说明：http://blog.csdn.net/c406495762/article/details/72597755\n\t\n\t第三方依赖库安装：\n\t\n\t\tpip3 install requests beautifulsoup4\n\t\t\n * daili.py: 构建代理IP池\n\n\t原理说明：http://blog.csdn.net/c406495762/article/details/72793480\n\t\n\t\n * carton: 使用Scrapy爬取《火影忍者》漫画\n\n\t代码可以爬取整个《火影忍者》漫画所有章节的内容，保存到本地。更改地址，可以爬取其他漫画。保存地址可以在settings.py中修改。\n\t\n\t动漫网站：http://comic.kukudm.com/\n\t\n\t原理说明：http://blog.csdn.net/c406495762/article/details/72858983\n\t\n * hero.py: 《王者荣耀》推荐出装查询小助手\n\n\t网页爬取已经会了，想过爬取手机APP里的内容吗？\n\t\n\t原理说明：http://blog.csdn.net/c406495762/article/details/76850843\n\t\n * financical.py: 财务报表下载小助手\n\n\t爬取的数据存入数据库会吗？《跟股神巴菲特学习炒股之财务报表入库(MySQL)》也许能给你一些思路。\n\t\n\t原理说明：http://blog.csdn.net/c406495762/article/details/77801899\n\t\n\t动态示意图：\n\t\n\t![image](https://raw.githubusercontent.com/Jack-Cherish/Pictures/master/10.gif)\n\t\n * one_hour_spider:一小时入门Python3网络爬虫。\n\n\t原理说明:\n\t\n\t * 知乎：https://zhuanlan.zhihu.com/p/29809609\n\t * CSDN：http://blog.csdn.net/c406495762/article/details/78123502\n\t\n\t本次实战内容有：\n\t\n\t * 网络小说下载(静态网站)-biqukan\n\t * 优美壁纸下载(动态网站)-unsplash\n\t * 视频下载\n\t \n * douyin.py:抖音App视频下载\n \n\t抖音App的视频下载，就是普通的App爬取。\n\n\t原理说明:\n\t\n\t * 个人网站：http://cuijiahua.com/blog/2018/03/spider-5.html\n\t\n * douyin_pro:抖音App视频下载（升级版）\n \n\t抖音App的视频下载，添加视频解析网站，支持无水印视频下载，使用第三方平台解析。\n\n\t原理说明:\n\t\n\t * 个人网站：http://cuijiahua.com/blog/2018/03/spider-5.html\n\t \n * douyin:抖音App视频下载（升级版2）\n \n\t抖音App的视频下载，添加视频解析网站，支持无水印视频下载，通过url解析，无需第三方平台。\n\t\n\t原理说明:\n\t\n\t * 个人网站：http://cuijiahua.com/blog/2018/03/spider-5.html\n\t \n\t动态示意图：\n\t\n\t![image](https://github.com/Jack-Cherish/Pictures/blob/master/14.gif)\n\t\n * geetest.py:GEETEST验证码识别\n \n \t原理说明:\n\t\n\t 无\n\t\n * 12306.py:用Python抢火车票简单代码\n \n\t可以自己慢慢丰富，蛮简单，有爬虫基础很好操作，没有原理说明。\n\t\n * baiwan:百万英雄辅助答题\n \n\t效果图：\n\t\n\t![image](https://github.com/Jack-Cherish/Pictures/blob/master/11.gif)\n\t\n\t原理说明：\n\t\n\t* 个人网站：http://cuijiahua.com/blog/2018/01/spider_3.html\n\t\n  \t功能介绍：\n\t\n\t服务器端，使用Python（baiwan.py）通过抓包获得的接口获取答题数据，解析之后通过百度知道搜索接口匹配答案，将最终匹配的结果写入文件（file.txt)。\n\t\n\t手机抓包不会的朋友，可以看下我的早期[手机APP抓包教程](http://blog.csdn.net/c406495762/article/details/76850843 \"悬停显示\")。\n\t\n\tNode.js（app.js）每隔1s读取一次file.txt文件，并将读取结果通过socket.io推送给客户端（index.html）。\n\t\n\t亲测答题延时在3s左右。\n\t\n\t声明：没做过后端和前端，花了一天时间，现学现卖弄好的，javascript也是现看现用，百度的程序，调试调试而已。可能有很多用法比较low的地方，用法不对，请勿见怪，有大牛感兴趣，可以自行完善。\n\n * Netease:根据歌单下载网易云音乐\n \t\n\t效果图：\n\t\n\t![image](https://github.com/Jack-Cherish/Pictures/blob/master/13.gif)\n\t\n\t原理说明：\n\t\n\t暂无\n\t\n\t功能介绍：\n\t\n\t根据music_list.txt文件里的歌单的信息下载网易云音乐，将自己喜欢的音乐进行批量下载。\n\n * bilibili：B站视频和弹幕批量下载\n \t\n\t原理说明：\n\t\n\t暂无\n\t\n\t使用说明：\n\t\n        python bilibili.py -d 猫 -k 猫 -p 10\n\n        三个参数：\n        -d\t保存视频的文件夹名\n        -k\tB站搜索的关键字\n        -p\t下载搜索结果前多少页\n\t\n * jingdong：京东商品晒单图下载\n \n \t效果图：\n\t\n\t![image](https://github.com/Jack-Cherish/Pictures/blob/master/jd.gif)\n \t\n\t原理说明：\n\t\n\t暂无\n\t\n\t使用说明：\n\t\n        python jd.py -k 芒果\n\t\n         三个参数：\n        -d\t保存图片的路径，默认为fd.py文件所在文件夹\n        -k\t搜索关键词\n        -n  \t下载商品的晒单图个数，即n个商店的晒单图\n\n * zhengfang_system_spider：对正方教务管理系统个人课表，个人学生成绩，绩点等简单爬取\n \n \t效果图：\n\t\n\t![image](/zhengfang_system_spider/screenshot/zf.png)\n \t\n\t原理说明：\n\t\n\t暂无\n\t\n\t使用说明：\n\t\n        cd zhengfang_system_spider\n        pip install -r requirements.txt\n        python spider.py\n\n## 其它\n\n * 欢迎 Pull requests，感谢贡献。\n \n 更多精彩，敬请期待！\n\n<a name=\"微信\"></a>  <a name=\"公众号\"></a>\n\n<img src=\"https://ftp.bmp.ovh/imgs/2020/07/112254f0199e3d4f.jpg\" alt=\"wechat\" width=\"400\" height=\"200\" align=\"bottom\" />\n"
  },
  {
    "path": "baiduwenku.py",
    "content": "# -*- coding:UTF-8 -*-\nfrom selenium import webdriver\nfrom bs4 import BeautifulSoup\nimport re\nimport time\n\nif __name__ == '__main__':\n\n\toptions = webdriver.ChromeOptions()\n\toptions.add_argument('user-agent=\"Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19\"')\n\tdriver = webdriver.Chrome('J:\\迅雷下载\\chromedriver.exe', chrome_options=options)\n\tdriver.get('https://wenku.baidu.com/view/aa31a84bcf84b9d528ea7a2c.html')\n\n\thtml = driver.page_source\n\tbf1 = BeautifulSoup(html, 'lxml')\n\tresult = bf1.find_all(class_='rtcspage')\n\tbf2 = BeautifulSoup(str(result[0]), 'lxml')\n\ttitle = bf2.div.div.h1.string\n\tpagenum = bf2.find_all(class_='size')\n\tpagenum = BeautifulSoup(str(pagenum), 'lxml').span.string\n\tpagepattern = re.compile('页数：(\\d+)页')\n\tnum = int(pagepattern.findall(pagenum)[0])\n\tprint('文章标题：%s' % title)\n\tprint('文章页数：%d' % num)\n\n\n\twhile True:\n\t\tnum = num / 5.0\n\t\thtml = driver.page_source\n\t\tbf1 = BeautifulSoup(html, 'lxml')\n\t\tresult = bf1.find_all(class_='rtcspage')\n\t\tfor each_result in result:\n\t\t\tbf2 = BeautifulSoup(str(each_result), 'lxml')\n\t\t\ttexts = bf2.find_all('p')\n\t\t\tfor each_text in texts:\n\t\t\t\tmain_body = BeautifulSoup(str(each_text), 'lxml')\n\t\t\t\tfor each in main_body.find_all(True):\n\t\t\t\t\tif each.name == 'span':\n\t\t\t\t\t\tprint(each.string.replace('\\xa0',''),end='')\n\t\t\t\t\telif each.name == 'br':\n\t\t\t\t\t\tprint('')\n\t\t\tprint('\\n')\n\t\tif num > 1:\n\t\t\tpage = driver.find_elements_by_xpath(\"//div[@class='page']\")\n\t\t\tdriver.execute_script('arguments[0].scrollIntoView();', page[-1]) #拖动到可见的元素去\n\t\t\tnextpage = driver.find_element_by_xpath(\"//a[@data-fun='next']\")\n\t\t\tnextpage.click()\n\t\t\ttime.sleep(3)\n\t\telse:\n\t\t\tbreak"
  },
  {
    "path": "baiduwenku_pro_1.py",
    "content": "import requests\nimport re\nimport json\nimport os\n\nsession = requests.session()\n\n\ndef fetch_url(url):\n    return session.get(url).content.decode('gbk')\n\n\ndef get_doc_id(url):\n    return re.findall('view/(.*).html', url)[0]\n\n\ndef parse_type(content):\n    return re.findall(r\"docType.*?\\:.*?\\'(.*?)\\'\\,\", content)[0]\n\n\ndef parse_title(content):\n    return re.findall(r\"title.*?\\:.*?\\'(.*?)\\'\\,\", content)[0]\n\n\ndef parse_doc(content):\n    result = ''\n    url_list = re.findall('(https.*?0.json.*?)\\\\\\\\x22}', content)\n    url_list = [addr.replace(\"\\\\\\\\\\\\/\", \"/\") for addr in url_list]\n    for url in url_list[:-5]:\n        content = fetch_url(url)\n        y = 0\n        txtlists = re.findall('\"c\":\"(.*?)\".*?\"y\":(.*?),', content)\n        for item in txtlists:\n            if not y == item[1]:\n                y = item[1]\n                n = '\\n'\n            else:\n                n = ''\n            result += n\n            result += item[0].encode('utf-8').decode('unicode_escape', 'ignore')\n    return result\n\n\ndef parse_txt(doc_id):\n    content_url = 'https://wenku.baidu.com/api/doc/getdocinfo?callback=cb&doc_id=' + doc_id\n    content = fetch_url(content_url)\n    md5 = re.findall('\"md5sum\":\"(.*?)\"', content)[0]\n    pn = re.findall('\"totalPageNum\":\"(.*?)\"', content)[0]\n    rsign = re.findall('\"rsign\":\"(.*?)\"', content)[0]\n    content_url = 'https://wkretype.bdimg.com/retype/text/' + doc_id + '?rn=' + pn + '&type=txt' + md5 + '&rsign=' + rsign\n    content = json.loads(fetch_url(content_url))\n    result = ''\n    for item in content:\n        for i in item['parags']:\n            result += i['c'].replace('\\\\r', '\\r').replace('\\\\n', '\\n')\n    return result\n\n\ndef parse_other(doc_id):\n    content_url = \"https://wenku.baidu.com/browse/getbcsurl?doc_id=\" + doc_id + \"&pn=1&rn=99999&type=ppt\"\n    content = fetch_url(content_url)\n    url_list = re.findall('{\"zoom\":\"(.*?)\",\"page\"', content)\n    url_list = [item.replace(\"\\\\\", '') for item in url_list]\n    if not os.path.exists(doc_id):\n        os.mkdir(doc_id)\n    for index, url in enumerate(url_list):\n        content = session.get(url).content\n        path = os.path.join(doc_id, str(index) + '.jpg')\n        with open(path, 'wb') as f:\n            f.write(content)\n    print(\"图片保存在\" + doc_id + \"文件夹\")\n\n\ndef save_file(filename, content):\n    with open(filename, 'w', encoding='utf8') as f:\n        f.write(content)\n        print('已保存为:' + filename)\n\n\n# test_txt_url = 'https://wenku.baidu.com/view/cbb4af8b783e0912a3162a89.html?from=search'\n# test_ppt_url = 'https://wenku.baidu.com/view/2b7046e3f78a6529657d5376.html?from=search'\n# test_pdf_url = 'https://wenku.baidu.com/view/dd6e15c1227916888586d795.html?from=search'\n# test_xls_url = 'https://wenku.baidu.com/view/eb4a5bb7312b3169a551a481.html?from=search'\ndef main():\n    url = input('请输入要下载的文库URL地址')\n    content = fetch_url(url)\n    doc_id = get_doc_id(url)\n    type = parse_type(content)\n    title = parse_title(content)\n    if type == 'doc':\n        result = parse_doc(content)\n        save_file(title + '.txt', result)\n    elif type == 'txt':\n        result = parse_txt(doc_id)\n        save_file(title + '.txt', result)\n    else:\n        parse_other(doc_id)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "baiwan/app.js",
    "content": "var http = require('http');\nvar fs = require('fs');\nvar schedule = require(\"node-schedule\"); \nvar message = {};\nvar count = 0;\nvar server = http.createServer(function (req,res){\n    fs.readFile('./index.html',function(error,data){\n        res.writeHead(200,{'Content-Type':'text/html'});\n        res.end(data,'utf-8');\n    });\n}).listen(80);\nconsole.log('Server running!');\nvar lineReader = require('line-reader');\nfunction messageGet(){\n    lineReader.eachLine('file.txt', function(line, last) {\n        count++;\n        var name = 'line' + count;\n        console.log(name);\n\tconsole.log(line);\n        message[name] = line;\n    });  \n    if(count == 25){\n    \tcount = 0;\n    }\n    else{\n    \tfor(var i = count+1; i <= 25; i++){\n  \t    var name = 'line' + i;\n            message[name] = 'f';\n\t}\n  \tcount = 0;\n    }\n}\nvar io = require('socket.io').listen(server);\nvar rule = new schedule.RecurrenceRule();\nvar times = [];\nfor(var i=1; i<1800; i++){\n    times.push(i);\n}\nrule.second = times;\nschedule.scheduleJob(rule, function(){\n        messageGet();\n});\nio.sockets.on('connection',function(socket){\n       // console.log('User connected' + count + 'user(s) present');\n        socket.emit('users',message);\n        socket.broadcast.emit('users',message);\n\n    socket.on('disconnect',function(){\n        console.log('User disconnected');\n        //socket.broadcast.emit('users',message);  \n    });\n});\n"
  },
  {
    "path": "baiwan/baiwan.py",
    "content": "# -*-coding:utf-8 -*-\nimport requests\nfrom lxml import etree\nfrom bs4 import BeautifulSoup\nimport urllib\nimport time, re, types, os\n\n\n\"\"\"\n代码写的匆忙，本来想再重构下，完善好注释再发，但是比较忙，想想算了，所以自行完善吧！写法很不规范，勿见怪。\n\n作者：  Jack Cui\nWebsite:http://cuijiahua.com\n注:     本软件仅用于学习交流，请勿用于任何商业用途！\n\"\"\"\n\nclass BaiWan():\n\tdef __init__(self):\n\t\t# 百度知道搜索接口\n\t\tself.baidu = 'http://zhidao.baidu.com/search?'\n\t\t# 百万英雄及接口,每个人的接口都不一样，里面包含的手机信息，因此不公布，请自行抓包，有疑问欢迎留言：http://cuijiahua.com/liuyan.html\n\t\tself.api = 'https://api-spe-ttl.ixigua.com/xxxxxxx={}'.format(int(time.time()*1000))\n\n\t# 获取答案并解析问题\n\tdef get_question(self):\n\t\tto = True\n\t\twhile to:\n\t\t\tlist_dir = os.listdir('./')\n\t\t\tif 'question.txt' not in list_dir:\n\t\t\t\tfw = open('question.txt', 'w')\n\t\t\t\tfw.write('百万英雄尚未出题请稍后!')\n\t\t\t\tfw.close()\t\t\n\t\t\tgo = True\n\t\t\twhile go:\n\t\t\t\treq = requests.get(self.api, verify=False)\n\t\t\t\treq.encoding = 'utf-8'\n\t\t\t\thtml = req.text\n\n\t\t\t\tprint(html)\n\t\t\t\tif '*' in html:\n\t\t\t\t\tquestion_start = html.index('*')\n\t\t\t\t\ttry:\n\t\t\t\t\t\t\n\t\t\t\t\t\tquestion_end = html.index('？')\n\t\t\t\t\texcept:\n\t\t\t\t\t\tquestion_end = html.index('?')\n\t\t\t\t\tquestion = html[question_start:question_end][2:]\n\t\t\t\t\tif question != None:\n\t\t\t\t\t\tfr = open('question.txt', 'r')\n\t\t\t\t\t\ttext = fr.readline()\n\t\t\t\t\t\tfr.close()\n\t\t\t\t\t\tif text != question:\n\t\t\t\t\t\t\tprint(question)\n\t\t\t\t\t\t\tgo = False\n\t\t\t\t\t\t\twith open('question.txt', 'w') as f:\n\t\t\t\t\t\t\t\tf.write(question)\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\ttime.sleep(1)\n\t\t\t\t\telse:\n\t\t\t\t\t\tto = False\n\t\t\t\telse:\n\t\t\t\t\tto = False\n\n\t\t\ttemp = re.findall(r'[\\u4e00-\\u9fa5a-zA-Z0-9\\+\\-\\*/]', html[question_end+1:])\n\t\t\tb_index = []\n\t\t\tprint(temp)\n\n\t\t\tfor index, each in enumerate(temp):\n\t\t\t\tif each == 'B':\n\t\t\t\t\tb_index.append(index)\n\t\t\t\telif each == 'P' and (len(temp) - index) <= 3 :\n\t\t\t\t\tb_index.append(index)\n\t\t\t\t\tbreak\n\n\t\t\tif len(b_index) == 4:\n\t\t\t\ta = ''.join(temp[b_index[0] + 1:b_index[1]])\n\t\t\t\tb = ''.join(temp[b_index[1] + 1:b_index[2]])\n\t\t\t\tc = ''.join(temp[b_index[2] + 1:b_index[3]])\n\t\t\t\talternative_answers = [a,b,c]\n\n\t\t\t\tif '下列' in question:\n\t\t\t\t\tquestion = a + ' ' + b + ' ' + c + ' ' + question.replace('下列', '')\n\t\t\t\telif '以下' in question:\n\t\t\t\t\tquestion = a + ' ' + b + ' ' + c + ' ' + question.replace('以下', '')\n\t\t\telse:\n\t\t\t\talternative_answers = []\n\t\t\t# 根据问题和备选答案搜索答案\n\t\t\tself.search(question, alternative_answers)\n\t\t\ttime.sleep(1)\n\n\tdef search(self, question, alternative_answers):\n\t\tprint(question)\n\t\tprint(alternative_answers)\n\t\tinfos = {\"word\":question}\n\t\t# 调用百度接口\n\t\turl = self.baidu + 'lm=0&rn=10&pn=0&fr=search&ie=gbk&' + urllib.parse.urlencode(infos, encoding='GB2312')\n\t\tprint(url)\n\t\theaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36',\n\t\t}\n\t\tsess = requests.Session()\n\t\treq = sess.get(url = url, headers=headers, verify=False)\n\t\treq.encoding = 'gbk'\n\t\t# print(req.text)\n\t\tbf = BeautifulSoup(req.text, 'lxml')\n\t\tanswers = bf.find_all('dd',class_='dd answer')\n\t\tfor answer in answers:\n\t\t\tprint(answer.text)\n\n\t\t# 推荐答案\n\t\trecommend = ''\n\t\tif alternative_answers != []:\n\t\t\tbest = []\n\t\t\tprint('\\n')\n\t\t\tfor answer in answers:\n\t\t\t\t# print(answer.text)\n\t\t\t\tfor each_answer in alternative_answers:\n\t\t\t\t\tif each_answer in answer.text:\n\t\t\t\t\t\tbest.append(each_answer)\n\t\t\t\t\t\tprint(each_answer,end=' ')\n\t\t\t\t\t\t# print(answer.text)\n\t\t\t\t\t\tprint('\\n')\n\t\t\t\t\t\tbreak\n\t\t\tstatistics = {}\n\t\t\tfor each in best:\n\t\t\t\tif each not in statistics.keys():\n\t\t\t\t\tstatistics[each] = 1\n\t\t\t\telse:\n\t\t\t\t\tstatistics[each] += 1\n\t\t\terrors = ['没有', '不是', '不对', '不正确','错误','不包括','不包含','不在','错']\n\t\t\terror_list = list(map(lambda x: x in question, errors))\n\t\t\tprint(error_list)\n\t\t\tif sum(error_list) >= 1:\n\t\t\t\tfor each_answer in alternative_answers:\n\t\t\t\t\tif each_answer not in statistics.items():\n\t\t\t\t\t\trecommend = each_answer\n\t\t\t\t\t\tprint('推荐答案：', recommend)\n\t\t\t\t\t\tbreak\n\t\t\telif statistics != {}:\n\t\t\t\trecommend = sorted(statistics.items(), key=lambda e:e[1], reverse=True)[0][0]\n\t\t\t\tprint('推荐答案：', recommend)\n\n\t\t# 写入文件\n\t\twith open('file.txt', 'w') as f:\n\t\t\tf.write('问题：' + question)\n\t\t\tf.write('\\n')\n\t\t\tf.write('*' * 50)\n\t\t\tf.write('\\n')\n\t\t\tif alternative_answers != []:\n\t\t\t\tf.write('选项：')\n\t\t\t\tfor i in range(len(alternative_answers)):\n\t\t\t\t\tf.write(alternative_answers[i])\n\t\t\t\t\tf.write('  ')\n\t\t\tf.write('\\n')\n\t\t\tf.write('*' * 50)\n\t\t\tf.write('\\n')\n\t\t\tf.write('参考答案：\\n')\n\t\t\tfor answer in answers:\n\t\t\t\tf.write(answer.text)\n\t\t\t\tf.write('\\n')\n\t\t\tf.write('*' * 50)\n\t\t\tf.write('\\n')\n\t\t\tif recommend != '':\n\t\t\t\tf.write('最终答案请自行斟酌！\\t')\n\t\t\t\tf.write('推荐答案：' + sorted(statistics.items(), key=lambda e:e[1], reverse=True)[0][0])\n\n\nif __name__ == '__main__':\n\tbw = BaiWan()\n\tbw.get_question()"
  },
  {
    "path": "baiwan/file.txt",
    "content": "⣺Ǽ¼\n**************************************************\nѡ723  81  101  \n**************************************************\nο𰸣\n\nƼ\n81 ÿİһйžգҲСһڡ August 1, anniversary of the founding of the Chinese People's Liberation Army֪Ⱦ뵽http://baike.baidu.com/view/23211.htm\n[ϸ]\n\nãйžĽÿİһգưһڣİһպ\n𣺽81գ71ա ÿ81йžգ׳ơһڡ192781գй챱ˣܶ  Ҷͦ  е쵼£ڽϲװ壬췴Թ񵳷...\n730\n𣺰һǽڣǰһ첻731ô\n192781һϲ,йװ񵳷ɵĵһǹ,־йй쵼װʱ,־й͵ӵĵÿİһйž\nԴйʱй쵼ϲ塣192781յϲ壬йװ񵳷ɵĵһǹ־йй쵼װʱڣ־й͵ӵĵ 19337£...\nԪ1181101\n𣺰һŽ\n 201581 ũ ʮ  201681 ũ إ  ÿİһйžգҲСһڡ1933711գлά͹ʱίԱ630յĽ飬81...\n**************************************************\nմã\tƼ𰸣81"
  },
  {
    "path": "baiwan/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"2\">\n    <title>Jack Cui答题辅助系统</title>\n  </head>\n  <body>\n    <h1>百万英雄答题辅助系统</h1>\n    <p id=\"line1\"></p>\n    <p id=\"line2\"></p>\n    <p id=\"line3\"></p>\n    <p id=\"line4\"></p>\n    <p id=\"line5\"></p>\n    <p id=\"line6\"></p>\n    <p id=\"line7\"></p>\n    <p id=\"line8\"></p>\n    <p id=\"line9\"></p>\n    <p id=\"line10\"></p>\n    <p id=\"line11\"></p>\n    <p id=\"line12\"></p>\n    <p id=\"line13\"></p>\n    <p id=\"line14\"></p>\n    <p id=\"line15\"></p>\n    <p id=\"line16\"></p>\n    <p id=\"line17\"></p>\n    <p id=\"line18\"></p>\n    <p id=\"line19\"></p>\n    <p id=\"line20\"></p>\n    <p id=\"line21\"></p>\n    <p id=\"line22\"></p>\n    <p id=\"line23\"></p>\n    <p id=\"line24\"></p>\n    <p id=\"line25\"></p>\n    <script src=\"http://222.222.124.77:9001/jquery.min.js\"></script>\n    <script src=\"/socket.io/socket.io.js\"></script>\n    <script>\n      var socket = io.connect('http://你的IP:端口');\n      var line1 = document.getElementById('line1');\n      var line2 = document.getElementById('line2');\n      var line3 = document.getElementById('line3');\n      var line4 = document.getElementById('line4');\n      var line5 = document.getElementById('line5');\n      var line6 = document.getElementById('line6');\n      var line7 = document.getElementById('line7');\n      var line8 = document.getElementById('line8');\n      var line9 = document.getElementById('line9');\n      var line10 = document.getElementById('line10');\n      var line11 = document.getElementById('line11');\n      var line12 = document.getElementById('line12');\n      var line13 = document.getElementById('line13');\n      var line14 = document.getElementById('line14');\n      var line15 = document.getElementById('line15');\n      var line16 = document.getElementById('line16');\n      var line17 = document.getElementById('line17');\n      var line18 = document.getElementById('line18');\n      var line19 = document.getElementById('line19');\n      var line20 = document.getElementById('line20');\n      var line21 = document.getElementById('line21');\n      var line22 = document.getElementById('line22');\n      var line23 = document.getElementById('line23');\n      var line24 = document.getElementById('line24');\n      var line25 = document.getElementById('line25');\n      socket.on('users',function(data){\n        if(data.line1 == 'f'){\n           line1.innerHTML = '' \n        }\n        else{\n           line1.innerHTML = data.line1\n\t\t}\n        if(data.line2 == 'f'){\n           line2.innerHTML = '' \n        }\n        else{\n           line2.innerHTML = data.line2\n\t\t}\n        if(data.line3 == 'f'){\n           line3.innerHTML = '' \n        }\n        else{\n           line3.innerHTML = data.line3\n\t\t}\n        if(data.line4 == 'f'){\n           line4.innerHTML = '' \n        }\n        else{\n           line4.innerHTML = data.line4\n        }\n\t\tif(data.line5 == 'f'){\n           line5.innerHTML = '' \n        }\n        else{\n           line5.innerHTML = data.line5\n        }\n\t\tif(data.line6 == 'f'){\n           line6.innerHTML = '' \n        }\n\t\telse{\n           line6.innerHTML = data.line6\n        }\n\t\tif(data.line7 == 'f'){\n           line7.innerHTML = ''\n        }\n        else{\n           line7.innerHTML = data.line7\n        }\n\t\tif(data.line8 == 'f'){\n           line8.innerHTML = '' \n\t\t}\n\t\telse{\n\t\t   line8.innerHTML = data.line8\n\t\t}\n        if(data.line9 == 'f'){\n           line9.innerHTML = '' \n\t\t}\n\t\telse{\n\t\t   line9.innerHTML = data.line9\n\t\t}\n        if(data.line10 == 'f'){\n           line10.innerHTML = '' \n        }\n        else{\n           line10.innerHTML = data.line10\n        }\n\t\tif(data.line11 == 'f'){\n           line11.innerHTML = '' \n        }\n        else{\n           line11.innerHTML = data.line11\n        }\n\t\tif(data.line12 == 'f'){\n           line12.innerHTML = '' \n        }\n        else{\n           line12.innerHTML = data.line12\n        }\n\t\tif(data.line13 == 'f'){\n           line13.innerHTML = '' \n        }\n        else{\n           line13.innerHTML = data.line13\n        }\n\t\tif(data.line14 == 'f'){\n           line14.innerHTML = '' \n        }\n        else{\n           line14.innerHTML = data.line14\n        }\n\t\tif(data.line15 == 'f'){\n           line15.innerHTML = '' \n        }\n        else{\n           line15.innerHTML = data.line15\n        }\n\t\tif(data.line16 == 'f'){\n           line16.innerHTML = ''\n        }\n        else{\n           line16.innerHTML = data.line16\n\t\t}\n        if(data.line17 == 'f'){\n           line17.innerHTML = '' \n\t\t}\n\t\telse{\n\t\t   line17.innerHTML = data.line17\n\t\t}\n        if(data.line18 == 'f'){\n           line18.innerHTML = '' \n        }\n        else{\n           line18.innerHTML = data.line18\n\t\t}\n        if(data.line19 == 'f'){\n           line19.innerHTML = '' \n        }\n        else{\n           line19.innerHTML = data.line19\n\t\t}\n        if(data.line20 == 'f'){\n           line20.innerHTML = '' \n        }\n        else{\n           line20.innerHTML = data.line20\n\t\t}\n        if(data.line21 == 'f'){\n           line21.innerHTML = '' \n        }\n        else{\n           line21.innerHTML = data.line21\n        }\n\t\tif(data.line22 == 'f'){\n           line22.innerHTML = '' \n        }\n        else{\n           line22.innerHTML = data.line22\n        }\n\t\tif(data.line23 == 'f'){\n           line23.innerHTML = '' \n        }\n\t\telse{\n           line23.innerHTML = data.line23\n        }\n\t\tif(data.line24 == 'f'){\n           line24.innerHTML = ''\n        }\n        else{\n           line24.innerHTML = data.line24\n        }\n\t\tif(data.line25 == 'f'){\n           line25.innerHTML = '' \n\t\t}\n\t\telse{\n\t\t   line25.innerHTML = data.line25\n\t\t}\n      });\n    </script>\n\n  </body>\n</html>\n"
  },
  {
    "path": "baiwan/question.txt",
    "content": "Ǽ¼"
  },
  {
    "path": "bilibili/README.md",
    "content": "## 功能\n\n下载B站视频和弹幕，将xml原生弹幕转换为ass弹幕文件，支持plotplayer等播放器的弹幕播放。\n\n## 作者\n\n* Website: [http://cuijiahua.com](http://cuijiahua.com \"悬停显示\")\n* Author: Jack Cui\n* Date: 2018.6.12\n\n## 更新\n\n* 2018.09.12：添加FFmpeg分段视频合并\n\n## 使用说明\n\nFFmpeg下载，并配置环境变量。http://ffmpeg.org/\n\n\tpython bilibili.py -d 猫 -k 猫 -p 10\n\n\t三个参数：\n\t-d\t保存视频的文件夹名\n\t-k\tB站搜索的关键字\n\t-p\t下载搜索结果前多少页\n"
  },
  {
    "path": "bilibili/bilibili.py",
    "content": "# -*-coding:utf-8 -*-\n# Website: http://cuijiahua.com\n# Author: Jack Cui\n# Date: 2018.6.9\n\nimport requests, json, re, sys, os, urllib, argparse, time\nfrom urllib.request import urlretrieve\nfrom contextlib import closing\nfrom urllib import parse\nimport xml2ass\n\nclass BiliBili:\n\tdef __init__(self, dirname, keyword):\n\t\tself.dn_headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n\t\t\t'Accept': '*/*',\n\t\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.9',\n\t\t\t'Referer': 'https://search.bilibili.com/all?keyword=%s' % parse.quote(keyword)}\n\n\t\tself.search_headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.9',\n\t\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t\t'Accept': 'application/json, text/plain, */*'}\n\n\t\tself.video_headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.9',\n\t\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t\t'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'}\n\n\t\tself.danmu_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n\t\t\t'Accept': '*/*',\n\t\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.9'}\n\n\t\tself.sess = requests.Session()\n\n\t\tself.dir = dirname\n\n\tdef video_downloader(self, video_url, video_name):\n\t\t\"\"\"\n\t\t视频下载\n\t\tParameters:\n\t\t\tvideo_url: 带水印的视频地址\n\t\t\tvideo_name: 视频名\n\t\tReturns:\n\t\t\t无\n\t\t\"\"\"\n\t\tsize = 0\n\t\twith closing(self.sess.get(video_url, headers=self.dn_headers, stream=True, verify=False)) as response:\n\t\t\tchunk_size = 1024\n\t\t\tcontent_size = int(response.headers['content-length'])\n\t\t\tif response.status_code == 200:\n\t\t\t\tsys.stdout.write('  [文件大小]:%0.2f MB\\n' % (content_size / chunk_size / 1024))\n\t\t\t\tvideo_name = os.path.join(self.dir, video_name)\n\t\t\t\twith open(video_name, 'wb') as file:\n\t\t\t\t\tfor data in response.iter_content(chunk_size = chunk_size):\n\t\t\t\t\t\tfile.write(data)\n\t\t\t\t\t\tsize += len(data)\n\t\t\t\t\t\tfile.flush()\n\n\t\t\t\t\t\tsys.stdout.write('  [下载进度]:%.2f%%' % float(size / content_size * 100) + '\\r')\n\t\t\t\t\t\t# sys.stdout.flush()\n\t\t\t\t\t\tif size / content_size == 1:\n\t\t\t\t\t\t\tprint('\\n')\n\t\t\telse:\n\t\t\t\tprint('链接异常')\n\n\tdef search_video(self, search_url):\n\t\t\"\"\"\n\t\t搜索接口\n\t\tParameters:\n\t\t\tsearch_url: 带水印的视频地址\n\t\tReturns:\n\t\t\ttitles：视频名列表\n\t\t\tarcurls: 视频播放地址列表\n\t\t\"\"\"\n\t\treq = self.sess.get(url=search_url, headers=self.search_headers, verify=False)\n\t\thtml = json.loads(req.text)\n\t\tvideos = html[\"data\"]['result']\n\t\ttitles = []\n\t\tarcurls = []\n\t\tfor video in videos:\n\t\t\ttitles.append(video['title'].replace('<em class=\"keyword\">','').replace('</em>',''))\n\t\t\tarcurls.append(video['arcurl'])\n\t\treturn titles, arcurls\n\n\tdef get_download_url(self, arcurl):\n\t\t\"\"\"\n\t\t获取视频下载地址\n\t\tParameters:\n\t\t\tarcurl: 视频播放地址\n\t\t\toid：弹幕地址参数\n\t\tReturns:\n\t\t\tdownload_url：视频下载地址\n\t\t\"\"\"\n\t\treq = self.sess.get(url=arcurl, headers=self.video_headers, verify=False)\n\t\tpattern = '.__playinfo__=(.*)</script><script>window.__INITIAL_STATE__='\n\t\ttry:\n\t\t\tinfos = re.findall(pattern, req.text)[0]\n\t\texcept:\n\t\t\treturn '',''\n\t\thtml = json.loads(infos)\n\t\tdurl = html['durl']\n\t\tdownload_url = []\n\t\tfor i in range(len(durl)):\n\t\t\tdownload_url.append(durl[i]['url'])\n\t\turl = durl[0]['url']\n\t\tif 'mirrork' in url:\n\t\t\toid = url.split('/')[6]\n\t\telse:\n\t\t\tid_ = url.split('/')[7]\n\t\t\tif len(id_) >= 10:\n\t\t\t\tid_ = url.split('/')[6]\n\t\t\toid = id_\n\t\treturn download_url, oid\n\n\n\tdef download_xml(self, danmu_url, danmu_name):\n\t\t\"\"\"\n\t\t获取视频XML原生弹幕\n\t\tParameters:\n\t\t\tdanmu_url: 弹幕地址\n\t\t\tdanmu_name：弹幕xml文件保存名\n\t\tReturns:\n\t\t\t无\n\t\t\"\"\"\n\t\twith closing(self.sess.get(danmu_url, headers=self.danmu_header, stream=True, verify=False)) as response:  \n\t\t\tif response.status_code == 200:\n\t\t\t\twith open(danmu_name, 'wb') as file:\n\t\t\t\t\tfor data in response.iter_content():\n\t\t\t\t\t\tfile.write(data)\n\t\t\t\t\t\tfile.flush()\n\t\t\telse:\n\t\t\t\tprint('链接异常')\n\n\tdef get_danmu(self, oid, filename):\n\t\t\"\"\"\n\t\t下载弹幕\n\t\tParameters:\n\t\t\toid: 弹幕oid\n\t\t\tfilename: 弹幕保存前缀名\n\t\tReturns:\n\t\t\t无\n\t\t\"\"\"\n\t\tdanmu_url = 'https://api.bilibili.com/x/v1/dm/list.so?oid={}'.format(oid)\n\t\tdanmu_name = os.path.join(self.dir, filename + '.xml')\n\t\tdanmu_ass = os.path.join(self.dir, filename + '.ass')\n\t\tself.download_xml(danmu_url, danmu_name)\n\t\ttime.sleep(0.5)\n\t\txml2ass.Danmaku2ASS(danmu_name, danmu_ass, 1280, 720)\n\t\t# os.remove(danmu_name)\n\n\tdef search_videos(self, keyword, pages):\n\t\t\"\"\"\n\t\t搜索视频\n\t\tParameters:\n\t\t\tkeyword: 搜索关键字\n\t\t\tpages：下载页数\n\t\tReturns:\n\t\t\t无\n\t\t\"\"\"\n\t\tif self.dir not in os.listdir():\n\t\t\tos.mkdir(self.dir)\n\t\tfor page in range(1, pages+1):\n\t\t\tsearch_url = 'https://api.bilibili.com/x/web-interface/search/type?jsonp=jsonp&search_type=video&keyword={}&page={}'.format(keyword, page)\n\t\t\ttitles, arcurls = self.search_video(search_url)\n\t\t\tfor index, arcurl in enumerate(arcurls):\n\t\t\t\ttitle = titles[index]\n\t\t\t\tfor c in u'´☆❤◦\\/:*?\"<>|':\n\t\t\t\t\ttitle = title.replace(c, '')\n\t\t\t\tif title + '.flv' not in os.listdir(self.dir):\n\t\t\t\t\tdownload_url, oid = self.get_download_url(arcurl)\n\t\t\t\t\tmovies = []\n\t\t\t\t\tfor i in range(len(download_url)):\n\t\t\t\t\t\tif download_url[i] != '' and oid != '':\n\t\t\t\t\t\t\tfname = title + '_' + str(i+1) + '.flv'\n\t\t\t\t\t\t\tmovies.append(fname)\n\t\t\t\t\t\t\tprint('第[ %d ]页:视频[ %s ]下载中:' % (page, fname))\n\t\t\t\t\t\t\tself.video_downloader(download_url[i], fname)\n\t\t\t\t\t\t\tprint('视频下载完成!')\n\t\t\t\t\tif len(movies) > 1:\n\t\t\t\t\t\tfilelist_fname = os.path.join(self.dir, 'filelist.txt')\n\t\t\t\t\t\twith open(filelist_fname, 'w') as f:\n\t\t\t\t\t\t\tfor flv in movies:\n\t\t\t\t\t\t\t\tf.write(\"file \" + flv)\n\t\t\t\t\t\t\t\tf.write('\\n')\n\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\tos.system('cd %s & ffmpeg -f concat -safe 0 -i %s -c copy %s' % (self.dir, 'filelist.txt', title + '.flv'))\n\t\t\t\t\t\texcept:\n\t\t\t\t\t\t\tprint('请安装FFmpeg,并配置环境变量 http://ffmpeg.org/')\n\t\t\t\t\t\tos.remove(filelist_fname)\n\t\t\t\t\t\tfor movie in movies:\n\t\t\t\t\t\t\tos.remove(os.path.join(self.dir, movie))\n\t\t\t\t\t\tprint('视频合并完成！')\n\t\t\t\t\tself.get_danmu(oid, title)\n\t\t\t\t\tprint('弹幕下载完成!')\n\nif __name__ == '__main__':\n\tif len(sys.argv) == 1:\n\t\tsys.argv.append('--help')\n\n\tparser = argparse.ArgumentParser()\n\tparser.add_argument('-d', '--dir', required=True, help=_('download path'))\n\tparser.add_argument('-k', '--keyword', required=True, help=_('search content'))\n\tparser.add_argument('-p', '--pages', required=True, help=_('the number of pages for downloading'), type=int, default=1)\n\t\n\targs = parser.parse_args()\n\tB = BiliBili(args.dir,args.keyword)\n\tB.search_videos(args.keyword, args.pages)\n\n\tprint('全部下载完成!')\n"
  },
  {
    "path": "bilibili/xml2ass.py",
    "content": "# The original author of this program, Danmaku2ASS, is StarBrilliant.\n# This file is released under General Public License version 3.\n# You should have received a copy of General Public License text alongside with\n# this program. If not, you can obtain it at http://gnu.org/copyleft/gpl.html .\n# This program comes with no warranty, the author will not be resopnsible for\n# any damage or problems caused by this program.\n\nimport argparse\nimport calendar\nimport gettext\nimport io\nimport json\nimport logging\nimport math\nimport os\nimport random\nimport re\nimport sys\nimport time\nimport xml.dom.minidom\n\n\nif sys.version_info < (3,):\n    raise RuntimeError('at least Python 3.0 is required')\n\ngettext.install('danmaku2ass', os.path.join(os.path.dirname(os.path.abspath(os.path.realpath(sys.argv[0] or 'locale'))), 'locale'))\n\ndef SeekZero(function):\n    def decorated_function(file_):\n        file_.seek(0)\n        try:\n            return function(file_)\n        finally:\n            file_.seek(0)\n    return decorated_function\n\n\ndef EOFAsNone(function):\n    def decorated_function(*args, **kwargs):\n        try:\n            return function(*args, **kwargs)\n        except EOFError:\n            return None\n    return decorated_function\n\n\n@SeekZero\n@EOFAsNone\ndef ProbeCommentFormat(f):\n    tmp = f.read(1)\n    if tmp == '[':\n        return 'Acfun'\n        # It is unwise to wrap a JSON object in an array!\n        # See this: http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/\n        # Do never follow what Acfun developers did!\n    elif tmp == '{':\n        tmp = f.read(14)\n        if tmp == '\"status_code\":':\n            return 'Tudou'\n        elif tmp == '\"root\":{\"total':\n            return 'sH5V'\n    elif tmp == '<':\n        tmp = f.read(1)\n        if tmp == '?':\n            tmp = f.read(38)\n            if tmp == 'xml version=\"1.0\" encoding=\"UTF-8\"?><p':\n                return 'Niconico'\n            elif tmp == 'xml version=\"1.0\" encoding=\"UTF-8\"?><i':\n                return 'Bilibili'\n            elif tmp == 'xml version=\"1.0\" encoding=\"utf-8\"?><i':\n                return 'Bilibili'  # tucao.cc, with the same file format as Bilibili\n            elif tmp == 'xml version=\"1.0\" encoding=\"Utf-8\"?>\\n<':\n                return 'Bilibili'  # Komica, with the same file format as Bilibili\n            elif tmp == 'xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<':\n                return 'MioMio'\n        elif tmp == 'p':\n            return 'Niconico'  # Himawari Douga, with the same file format as Niconico Douga\n\n\n#\n# ReadComments**** protocol\n#\n# Input:\n#     f:         Input file\n#     fontsize:  Default font size\n#\n# Output:\n#     yield a tuple:\n#         (timeline, timestamp, no, comment, pos, color, size, height, width)\n#     timeline:  The position when the comment is replayed\n#     timestamp: The UNIX timestamp when the comment is submitted\n#     no:        A sequence of 1, 2, 3, ..., used for sorting\n#     comment:   The content of the comment\n#     pos:       0 for regular moving comment,\n#                1 for bottom centered comment,\n#                2 for top centered comment,\n#                3 for reversed moving comment\n#     color:     Font color represented in 0xRRGGBB,\n#                e.g. 0xffffff for white\n#     size:      Font size\n#     height:    The estimated height in pixels\n#                i.e. (comment.count('\\n')+1)*size\n#     width:     The estimated width in pixels\n#                i.e. CalculateLength(comment)*size\n#\n# After implementing ReadComments****, make sure to update ProbeCommentFormat\n# and CommentFormatMap.\n#\n\n\ndef ReadCommentsNiconico(f, fontsize):\n    NiconicoColorMap = {'red': 0xff0000, 'pink': 0xff8080, 'orange': 0xffcc00, 'yellow': 0xffff00, 'green': 0x00ff00, 'cyan': 0x00ffff, 'blue': 0x0000ff, 'purple': 0xc000ff, 'black': 0x000000, 'niconicowhite': 0xcccc99, 'white2': 0xcccc99, 'truered': 0xcc0033, 'red2': 0xcc0033, 'passionorange': 0xff6600, 'orange2': 0xff6600, 'madyellow': 0x999900, 'yellow2': 0x999900, 'elementalgreen': 0x00cc66, 'green2': 0x00cc66, 'marineblue': 0x33ffcc, 'blue2': 0x33ffcc, 'nobleviolet': 0x6633cc, 'purple2': 0x6633cc}\n    dom = xml.dom.minidom.parse(f)\n    comment_element = dom.getElementsByTagName('chat')\n    for comment in comment_element:\n        try:\n            c = str(comment.childNodes[0].wholeText)\n            if c.startswith('/'):\n                continue  # ignore advanced comments\n            pos = 0\n            color = 0xffffff\n            size = fontsize\n            for mailstyle in str(comment.getAttribute('mail')).split():\n                if mailstyle == 'ue':\n                    pos = 1\n                elif mailstyle == 'shita':\n                    pos = 2\n                elif mailstyle == 'big':\n                    size = fontsize*1.44\n                elif mailstyle == 'small':\n                    size = fontsize*0.64\n                elif mailstyle in NiconicoColorMap:\n                    color = NiconicoColorMap[mailstyle]\n            yield (max(int(comment.getAttribute('vpos')), 0)*0.01, int(comment.getAttribute('date')), int(comment.getAttribute('no')), c, pos, color, size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %s') % comment.toxml())\n            continue\n\n\ndef ReadCommentsAcfun(f, fontsize):\n    comment_element = json.load(f)\n    for i, comment in enumerate(comment_element):\n        try:\n            p = str(comment['c']).split(',')\n            assert len(p) >= 6\n            assert p[2] in ('1', '2', '4', '5', '7')\n            size = int(p[3])*fontsize/25.0\n            if p[2] != '7':\n                c = str(comment['m']).replace('\\\\r', '\\n').replace('\\r', '\\n')\n                yield (float(p[0]), int(p[5]), i, c, {'1': 0, '2': 0, '4': 2, '5': 1}[p[2]], int(p[1]), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n            else:\n                c = dict(json.loads(comment['m']))\n                yield (float(p[0]), int(p[5]), i, c, 'acfunpos', int(p[1]), size, 0, 0)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %r') % comment)\n            continue\n\n\ndef ReadCommentsBilibili(f, fontsize):\n    dom = xml.dom.minidom.parse(f)\n    comment_element = dom.getElementsByTagName('d')\n    for i, comment in enumerate(comment_element):\n        try:\n            p = str(comment.getAttribute('p')).split(',')\n            assert len(p) >= 5\n            assert p[1] in ('1', '4', '5', '6', '7')\n            if p[1] != '7':\n                c = str(comment.childNodes[0].wholeText).replace('/n', '\\n')\n                size = int(p[2])*fontsize/25.0\n                yield (float(p[0]), int(p[4]), i, c, {'1': 0, '4': 2, '5': 1, '6': 3}[p[1]], int(p[3]), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n            else:  # positioned comment\n                c = str(comment.childNodes[0].wholeText)\n                yield (float(p[0]), int(p[4]), i, c, 'bilipos', int(p[3]), int(p[2]), 0, 0)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %s') % comment.toxml())\n            continue\n\n\ndef ReadCommentsTudou(f, fontsize):\n    comment_element = json.load(f)\n    for i, comment in enumerate(comment_element['comment_list']):\n        try:\n            assert comment['pos'] in (3, 4, 6)\n            c = str(comment['data'])\n            assert comment['size'] in (0, 1, 2)\n            size = {0: 0.64, 1: 1, 2: 1.44}[comment['size']]*fontsize\n            yield (int(comment['replay_time']*0.001), int(comment['commit_time']), i, c, {3: 0, 4: 2, 6: 1}[comment['pos']], int(comment['color']), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %r') % comment)\n            continue\n\n\ndef ReadCommentsMioMio(f, fontsize):\n    NiconicoColorMap = {'red': 0xff0000, 'pink': 0xff8080, 'orange': 0xffc000, 'yellow': 0xffff00, 'green': 0x00ff00, 'cyan': 0x00ffff, 'blue': 0x0000ff, 'purple': 0xc000ff, 'black': 0x000000}\n    dom = xml.dom.minidom.parse(f)\n    comment_element = dom.getElementsByTagName('data')\n    for i, comment in enumerate(comment_element):\n        try:\n            message = comment.getElementsByTagName('message')[0]\n            c = str(message.childNodes[0].wholeText)\n            pos = 0\n            size = int(message.getAttribute('fontsize'))*fontsize/25.0\n            yield (float(comment.getElementsByTagName('playTime')[0].childNodes[0].wholeText), int(calendar.timegm(time.strptime(comment.getElementsByTagName('times')[0].childNodes[0].wholeText, '%Y-%m-%d %H:%M:%S')))-28800, i, c, {'1': 0, '4': 2, '5': 1}[message.getAttribute('mode')], int(message.getAttribute('color')), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %s') % comment.toxml())\n            continue\n\n\ndef ReadCommentsSH5V(f, fontsize):\n    comment_element = json.load(f)\n    for i, comment in enumerate(comment_element[\"root\"][\"bgs\"]):\n        try:\n            c_at = str(comment['at'])\n            c_type = str(comment['type'])\n            c_date = str(comment['timestamp'])\n            c_color = str(comment['color'])\n            c = str(comment['text'])\n            size = fontsize\n            if c_type != '7':\n                yield (float(c_at), int(c_date), i, c, {'0': 0, '1': 0, '4': 2, '5': 1}[c_type], int(c_color[1:], 16), size, (c.count('\\n')+1)*size, CalculateLength(c)*size)\n            else:\n                c_x = float(comment['x'])\n                c_y = float(comment['y'])\n                size = int(comment['size'])\n                dur = int(comment['dur'])\n                data1 = float(comment['data1'])\n                data2 = float(comment['data2'])\n                data3 = int(comment['data3'])\n                data4 = int(comment['data4'])\n                yield (float(c_at), int(c_date), i, c, 'sH5Vpos', int(c_color[1:], 16), size, 0, 0, c_x, c_y, dur, data1, data2, data3, data4)\n        except (AssertionError, AttributeError, IndexError, TypeError, ValueError):\n            logging.warning(_('Invalid comment: %r') % comment)\n            continue\n\n\nCommentFormatMap = {None: None, 'Niconico': ReadCommentsNiconico, 'Acfun': ReadCommentsAcfun, 'Bilibili': ReadCommentsBilibili, 'Tudou': ReadCommentsTudou, 'MioMio': ReadCommentsMioMio, 'sH5V': ReadCommentsSH5V}\n\n\ndef WriteCommentBilibiliPositioned(f, c, width, height, styleid):\n    #BiliPlayerSize = (512, 384)  # Bilibili player version 2010\n    #BiliPlayerSize = (540, 384)  # Bilibili player version 2012\n    BiliPlayerSize = (672, 438)  # Bilibili player version 2014\n    ZoomFactor = GetZoomFactor(BiliPlayerSize, (width, height))\n\n    def GetPosition(InputPos, isHeight):\n        isHeight = int(isHeight)  # True -> 1\n        if isinstance(InputPos, int):\n            return ZoomFactor[0]*InputPos+ZoomFactor[isHeight+1]\n        elif isinstance(InputPos, float):\n            if InputPos > 1:\n                return ZoomFactor[0]*InputPos+ZoomFactor[isHeight+1]\n            else:\n                return BiliPlayerSize[isHeight]*ZoomFactor[0]*InputPos+ZoomFactor[isHeight+1]\n        else:\n            try:\n                InputPos = int(InputPos)\n            except ValueError:\n                InputPos = float(InputPos)\n            return GetPosition(InputPos, isHeight)\n\n    try:\n        comment_args = safe_list(json.loads(c[3]))\n        text = ASSEscape(str(comment_args[4]).replace('/n', '\\n'))\n        from_x = comment_args.get(0, 0)\n        from_y = comment_args.get(1, 0)\n        to_x = comment_args.get(7, from_x)\n        to_y = comment_args.get(8, from_y)\n        from_x = round(GetPosition(from_x, False))\n        from_y = round(GetPosition(from_y, True))\n        to_x = round(GetPosition(to_x, False))\n        to_y = round(GetPosition(to_y, True))\n        alpha = safe_list(str(comment_args.get(2, '1')).split('-'))\n        from_alpha = float(alpha.get(0, 1))\n        to_alpha = float(alpha.get(1, from_alpha))\n        from_alpha = 255-round(from_alpha*255)\n        to_alpha = 255-round(to_alpha*255)\n        rotate_z = int(comment_args.get(5, 0))\n        rotate_y = int(comment_args.get(6, 0))\n        lifetime = float(comment_args.get(3, 4500))\n        duration = int(comment_args.get(9, lifetime*1000))\n        delay = int(comment_args.get(10, 0))\n        fontface = comment_args.get(12)\n        isborder = comment_args.get(11, 'true')\n        styles = []\n        if (from_x, from_y) == (to_x, to_y):\n            styles.append('\\\\pos(%s, %s)' % (from_x, from_y))\n        else:\n            styles.append('\\\\move(%s, %s, %s, %s, %s, %s)' % (from_x, from_y, to_x, to_y, delay, delay+duration))\n        styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(rotate_y, rotate_z, (from_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (from_y-ZoomFactor[2])/(height-ZoomFactor[2]*2)))\n        if (from_x, from_y) != (to_x, to_y):\n            styles.append('\\\\t(%s, %s, ' % (delay, delay+duration))\n            styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(rotate_y, rotate_z, (to_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (to_y-ZoomFactor[2])/(height-ZoomFactor[2]*2)))\n            styles.append(')')\n        if fontface:\n            styles.append('\\\\fn%s' % ASSEscape(fontface))\n        styles.append('\\\\fs%s' % round(c[6]*ZoomFactor[0]))\n        if c[5] != 0xffffff:\n            styles.append('\\\\c&H%02X%02X%02X&' % (c[5] & 0xff, (c[5] >> 8) & 0xff, (c[5] >> 16) & 0xff))\n            if c[5] == 0x000000:\n                styles.append('\\\\3c&HFFFFFF&')\n        if from_alpha == to_alpha:\n            styles.append('\\\\alpha&H%02X' % from_alpha)\n        elif (from_alpha, to_alpha) == (255, 0):\n            styles.append('\\\\fad(%s,0)' % (lifetime*1000))\n        elif (from_alpha, to_alpha) == (0, 255):\n            styles.append('\\\\fad(0, %s)' % (lifetime*1000))\n        else:\n            styles.append('\\\\fade(%(from_alpha)s, %(to_alpha)s, %(to_alpha)s, 0, %(end_time)s, %(end_time)s, %(end_time)s)' % {'from_alpha': from_alpha, 'to_alpha': to_alpha, 'end_time': lifetime*1000})\n        if isborder == 'false':\n            styles.append('\\\\bord0')\n        f.write('Dialogue: -1,%(start)s,%(end)s,%(styleid)s,,0,0,0,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(c[0]), 'end': ConvertTimestamp(c[0]+lifetime), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n    except (IndexError, ValueError) as e:\n        try:\n            logging.warning(_('Invalid comment: %r') % c[3])\n        except IndexError:\n            logging.warning(_('Invalid comment: %r') % c)\n\n\ndef WriteCommentAcfunPositioned(f, c, width, height, styleid):\n    AcfunPlayerSize = (560, 400)\n    ZoomFactor = GetZoomFactor(AcfunPlayerSize, (width, height))\n\n    def GetPosition(InputPos, isHeight):\n        isHeight = int(isHeight)  # True -> 1\n        return AcfunPlayerSize[isHeight]*ZoomFactor[0]*InputPos*0.001+ZoomFactor[isHeight+1]\n\n    def GetTransformStyles(x=None, y=None, scale_x=None, scale_y=None, rotate_z=None, rotate_y=None, color=None, alpha=None):\n        styles = []\n        if x is not None and y is not None:\n            styles.append('\\\\pos(%s, %s)' % (x, y))\n        if scale_x is not None:\n            styles.append('\\\\fscx%s' % scale_x)\n        if scale_y is not None:\n            styles.append('\\\\fscy%s' % scale_y)\n        if rotate_z is not None and rotate_y is not None:\n            assert x is not None\n            assert y is not None\n            styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(rotate_y, rotate_z, (x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (y-ZoomFactor[2])/(height-ZoomFactor[2]*2)))\n        if color is not None:\n            styles.append('\\\\c&H%02X%02X%02X&' % (color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff))\n            if color == 0x000000:\n                styles.append('\\\\3c&HFFFFFF&')\n        if alpha is not None:\n            alpha = 255-round(alpha*255)\n            styles.append('\\\\alpha&H%02X' % alpha)\n        return styles\n\n    def FlushCommentLine(f, text, styles, start_time, end_time, styleid):\n        if end_time > start_time:\n            f.write('Dialogue: -1,%(start)s,%(end)s,%(styleid)s,,0,0,0,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(start_time), 'end': ConvertTimestamp(end_time), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n\n    try:\n        comment_args = c[3]\n        text = ASSEscape(str(comment_args['n']).replace('\\r', '\\n').replace('\\r', '\\n'))\n        common_styles = []\n        anchor = {0: 7, 1: 8, 2: 9, 3: 4, 4: 5, 5: 6, 6: 1, 7: 2, 8: 3}.get(comment_args.get('c', 0), 7)\n        if anchor != 7:\n            common_styles.append('\\\\an%s' % anchor)\n        font = comment_args.get('w')\n        if font:\n            font = dict(font)\n            fontface = font.get('f')\n            if fontface:\n                common_styles.append('\\\\fn%s' % ASSEscape(str(fontface)))\n            fontbold = bool(font.get('b'))\n            if fontbold:\n                common_styles.append('\\\\b1')\n        common_styles.append('\\\\fs%s' % round(c[6]*ZoomFactor[0]))\n        isborder = bool(comment_args.get('b', True))\n        if not isborder:\n            common_styles.append('\\\\bord0')\n        to_pos = dict(comment_args.get('p', {'x': 0, 'y': 0}))\n        to_x = round(GetPosition(int(to_pos.get('x', 0)), False))\n        to_y = round(GetPosition(int(to_pos.get('y', 0)), True))\n        to_scale_x = round(float(comment_args.get('e', 1.0))*100)\n        to_scale_y = round(float(comment_args.get('f', 1.0))*100)\n        to_rotate_z = float(comment_args.get('r', 0.0))\n        to_rotate_y = float(comment_args.get('k', 0.0))\n        to_color = c[5]\n        to_alpha = float(comment_args.get('a', 1.0))\n        from_time = float(comment_args.get('t', 0.0))\n        action_time = float(comment_args.get('l', 3.0))\n        actions = list(comment_args.get('z', []))\n        transform_styles = GetTransformStyles(to_x, to_y, to_scale_x, to_scale_y, to_rotate_z, to_rotate_y, to_color, to_alpha)\n        FlushCommentLine(f, text, common_styles+transform_styles, c[0]+from_time, c[0]+from_time+action_time, styleid)\n        for action in actions:\n            action = dict(action)\n            from_x, from_y = to_x, to_y\n            from_scale_x, from_scale_y = to_scale_x, to_scale_y\n            from_rotate_z, from_rotate_y = to_rotate_z, to_rotate_y\n            from_color, from_alpha = to_color, to_alpha\n            from_time += action_time\n            action_time = float(action.get('l', 0.0))\n            action_styles = []\n            if 'x' in action:\n                to_x = round(GetPosition(int(action['x']), False))\n            if 'y' in action:\n                to_y = round(GetPosition(int(action['y']), True))\n            if 'f' in action:\n                to_scale_x = round(float(action['f'])*100)\n                action_styles.append('\\\\fscx%s' % to_scale_x)\n            if 'g' in action:\n                to_scale_y = round(float(action['g'])*100)\n                action_styles.append('\\\\fscy%s' % to_scale_y)\n            if 'c' in action:\n                to_color = int(action['c'])\n                action_styles.append('\\\\c&H%02X%02X%02X&' % (to_color & 0xff, (to_color >> 8) & 0xff, (to_color >> 16) & 0xff))\n            if 't' in action:\n                to_alpha = float(action['t'])\n                action_styles.append('\\\\alpha&H%02X' % (255-round(to_alpha*255)))\n            if 'd' in action:\n                to_rotate_z = float(action['d'])\n            if 'e' in action:\n                to_rotate_y = float(action['e'])\n            if ('x' in action) or ('y' in action):\n                transform_styles = GetTransformStyles(None, None, from_scale_x, from_scale_y, None, None, from_color, from_alpha)\n                transform_styles.append('\\\\move(%s, %s, %s, %s)' % (from_x, from_y, to_x, to_y))\n                action_styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(to_rotate_y, to_rotate_z, (to_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (to_y-ZoomFactor[2])/(width-ZoomFactor[2]*2)))\n            elif ('d' in action) or ('e' in action):\n                action_styles.append('\\\\frx%s\\\\fry%s\\\\frz%s\\\\fax%s\\\\fay%s' % ConvertFlashRotation(to_rotate_y, to_rotate_z, (to_x-ZoomFactor[1])/(width-ZoomFactor[1]*2), (to_y-ZoomFactor[2])/(width-ZoomFactor[2]*2)))\n            else:\n                transform_styles = GetTransformStyles(from_x, from_y, from_scale_x, from_scale_y, from_rotate_z, from_rotate_y, from_color, from_alpha)\n            if action_styles:\n                transform_styles.append('\\\\t(%s)' % (''.join(action_styles)))\n            FlushCommentLine(f, text, common_styles+transform_styles, c[0]+from_time, c[0]+from_time+action_time, styleid)\n    except (IndexError, ValueError) as e:\n        logging.warning(_('Invalid comment: %r') % c[3])\n\n\ndef WriteCommentSH5VPositioned(f, c, width, height, styleid):\n\n    def GetTransformStyles(x=None, y=None, fsize=None, rotate_z=None, rotate_y=None, color=None, alpha=None):\n        styles = []\n        if x is not None and y is not None:\n            styles.append('\\\\pos(%s, %s)' % (x, y))\n        if fsize is not None:\n            styles.append('\\\\fs%s' % fsize)\n        if rotate_y is not None and rotate_z is not None:\n            styles.append('\\\\frz%s' % round(rotate_z))\n            styles.append('\\\\fry%s' % round(rotate_y))\n        if color is not None:\n            styles.append('\\\\c&H%02X%02X%02X&' % (color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff))\n            if color == 0x000000:\n                styles.append('\\\\3c&HFFFFFF&')\n        if alpha is not None:\n            alpha = 255-round(alpha*255)\n            styles.append('\\\\alpha&H%02X' % alpha)\n        return styles\n\n    def FlushCommentLine(f, text, styles, start_time, end_time, styleid):\n        if end_time > start_time:\n            f.write('Dialogue: -1,%(start)s,%(end)s,%(styleid)s,,0,0,0,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(start_time), 'end': ConvertTimestamp(end_time), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n\n    try:\n        text = ASSEscape(str(c[3]))\n        to_x = round(float(c[9])*width)\n        to_y = round(float(c[10])*height)\n        to_rotate_z = -int(c[14])\n        to_rotate_y = -int(c[15])\n        to_color = c[5]\n        to_alpha = float(c[12])\n        #Note: Alpha transition hasn't been worked out yet.\n        to_size = round(int(c[6])*math.sqrt(width*height/307200))\n        #Note: Because sH5V's data is the absolute size of font,temporarily solve by it at present.[*math.sqrt(width/640*height/480)]\n        #But it seems to be working fine...\n        from_time = float(c[0])\n        action_time = float(c[11])/1000\n        transform_styles = GetTransformStyles(to_x, to_y, to_size, to_rotate_z, to_rotate_y, to_color, to_alpha)\n        FlushCommentLine(f, text, transform_styles, from_time, from_time+action_time, styleid)\n    except (IndexError, ValueError) as e:\n        logging.warning(_('Invalid comment: %r') % c[3])\n\n\n# Result: (f, dx, dy)\n# To convert: NewX = f*x+dx, NewY = f*y+dy\ndef GetZoomFactor(SourceSize, TargetSize):\n    try:\n        if (SourceSize, TargetSize) == GetZoomFactor.Cached_Size:\n            return GetZoomFactor.Cached_Result\n    except AttributeError:\n        pass\n    GetZoomFactor.Cached_Size = (SourceSize, TargetSize)\n    try:\n        SourceAspect = SourceSize[0]/SourceSize[1]\n        TargetAspect = TargetSize[0]/TargetSize[1]\n        if TargetAspect < SourceAspect:  # narrower\n            ScaleFactor = TargetSize[0]/SourceSize[0]\n            GetZoomFactor.Cached_Result = (ScaleFactor, 0, (TargetSize[1]-TargetSize[0]/SourceAspect)/2)\n        elif TargetAspect > SourceAspect:  # wider\n            ScaleFactor = TargetSize[1]/SourceSize[1]\n            GetZoomFactor.Cached_Result = (ScaleFactor, (TargetSize[0]-TargetSize[1]*SourceAspect)/2, 0)\n        else:\n            GetZoomFactor.Cached_Result = (TargetSize[0]/SourceSize[0], 0, 0)\n        return GetZoomFactor.Cached_Result\n    except ZeroDivisionError:\n        GetZoomFactor.Cached_Result = (1, 0, 0)\n        return GetZoomFactor.Cached_Result\n\n\n# Calculation is based on https://github.com/jabbany/CommentCoreLibrary/issues/5#issuecomment-40087282\n#                     and https://github.com/m13253/danmaku2ass/issues/7#issuecomment-41489422\n# Input: X relative horizonal coordinate: 0 for left edge, 1 for right edge.\n#        Y relative vertical coordinate: 0 for top edge, 1 for bottom edge.\n# FOV = 1.0/math.tan(100*math.pi/360.0)\n# Result: (rotX, rotY, rotZ, shearX, shearY)\ndef ConvertFlashRotation(rotY, rotZ, X, Y, FOV=math.tan(2*math.pi/9.0)):\n    def WrapAngle(deg):\n        return 180-((180-deg)%360)\n    def CalcPerspectiveCorrection(alpha, X, FOV=FOV):\n        alpha = WrapAngle(alpha)\n        if FOV is None:\n            return alpha\n        if 0 <= alpha <= 180:\n            costheta = (FOV*math.cos(alpha*math.pi/180.0)-X*math.sin(alpha*math.pi/180.0))/(FOV+max(2, abs(X)+1)*math.sin(alpha*math.pi/180.0))\n            try:\n                if costheta > 1:\n                    costheta = 1\n                    raise ValueError\n                elif costheta < -1:\n                    costheta = -1\n                    raise ValueError\n            except ValueError:\n                logging.error('Clipped rotation angle: (alpha=%s, X=%s), it is a bug!' % (alpha, X))\n            theta = math.acos(costheta)*180/math.pi\n        else:\n            costheta = (FOV*math.cos(alpha*math.pi/180.0)-X*math.sin(alpha*math.pi/180.0))/(FOV-max(2, abs(X)+1)*math.sin(alpha*math.pi/180.0))\n            try:\n                if costheta > 1:\n                    costheta = 1\n                    raise ValueError\n                elif costheta < -1:\n                    costheta = -1\n                    raise ValueError\n            except ValueError:\n                logging.error('Clipped rotation angle: (alpha=%s, X=%s), it is a bug!' % (alpha, X))\n            theta = -math.acos(costheta)*180/math.pi\n        return WrapAngle(theta)\n    X = 2*X-1\n    Y = 2*Y-1\n    rotY = WrapAngle(rotY)\n    rotZ = WrapAngle(rotZ)\n    if rotY == 0 or rotZ == 0:\n        outX = 0\n        outY = -rotY  # Positive value means clockwise in Flash\n        outZ = -rotZ\n    else:\n        rotY = rotY*math.pi/180.0\n        rotZ = rotZ*math.pi/180.0\n        outY = math.atan2(-math.sin(rotY)*math.cos(rotZ), math.cos(rotY))*180/math.pi\n        outZ = math.atan2(-math.cos(rotY)*math.sin(rotZ), math.cos(rotZ))*180/math.pi\n        outX = math.asin(math.sin(rotY)*math.sin(rotZ))*180/math.pi\n    if FOV is not None:\n        #outX = CalcPerspectiveCorrection(outX, -Y, FOV*0.75)\n        outY = CalcPerspectiveCorrection(outY, X, FOV)\n    return (WrapAngle(round(outX)), WrapAngle(round(outY)), WrapAngle(round(outZ)), 0, round(-0.75*Y*math.sin(outY*math.pi/180.0), 3))\n\n\ndef ProcessComments(comments, f, width, height, bottomReserved, fontface, fontsize, alpha, lifetime, reduced, progress_callback):\n    styleid = 'Danmaku2ASS_%04x' % random.randint(0, 0xffff)\n    WriteASSHead(f, width, height, fontface, fontsize, alpha, styleid)\n    rows = [[None]*(height-bottomReserved+1) for i in range(4)]\n    for idx, i in enumerate(comments):\n        if progress_callback and idx % 1000 == 0:\n            progress_callback(idx, len(comments))\n        if isinstance(i[4], int):\n            row = 0\n            rowmax = height-bottomReserved-i[7]\n            while row <= rowmax:\n                freerows = TestFreeRows(rows, i, row, width, height, bottomReserved, lifetime)\n                if freerows >= i[7]:\n                    MarkCommentRow(rows, i, row)\n                    WriteComment(f, i, row, width, height, bottomReserved, fontsize, lifetime, styleid)\n                    break\n                else:\n                    row += freerows or 1\n            else:\n                if not reduced:\n                    row = FindAlternativeRow(rows, i, height, bottomReserved)\n                    MarkCommentRow(rows, i, row)\n                    WriteComment(f, i, row, width, height, bottomReserved, fontsize, lifetime, styleid)\n        elif i[4] == 'bilipos':\n            WriteCommentBilibiliPositioned(f, i, width, height, styleid)\n        elif i[4] == 'acfunpos':\n            WriteCommentAcfunPositioned(f, i, width, height, styleid)\n        elif i[4] == 'sH5Vpos':\n            WriteCommentSH5VPositioned(f, i, width, height, styleid)\n        else:\n            logging.warning(_('Invalid comment: %r') % i[3])\n    if progress_callback:\n        progress_callback(len(comments), len(comments))\n\n\ndef TestFreeRows(rows, c, row, width, height, bottomReserved, lifetime):\n    res = 0\n    rowmax = height-bottomReserved\n    targetRow = None\n    if c[4] in (1, 2):\n        while row < rowmax and res < c[7]:\n            if targetRow != rows[c[4]][row]:\n                targetRow = rows[c[4]][row]\n                if targetRow and targetRow[0]+lifetime > c[0]:\n                    break\n            row += 1\n            res += 1\n    else:\n        try:\n            thresholdTime = c[0]-lifetime*(1-width/(c[8]+width))\n        except ZeroDivisionError:\n            thresholdTime = c[0]-lifetime\n        while row < rowmax and res < c[7]:\n            if targetRow != rows[c[4]][row]:\n                targetRow = rows[c[4]][row]\n                try:\n                    if targetRow and (targetRow[0] > thresholdTime or targetRow[0]+targetRow[8]*lifetime/(targetRow[8]+width) > c[0]):\n                        break\n                except ZeroDivisionError:\n                    pass\n            row += 1\n            res += 1\n    return res\n\n\ndef FindAlternativeRow(rows, c, height, bottomReserved):\n    res = 0\n    for row in range(height-bottomReserved-math.ceil(c[7])):\n        if not rows[c[4]][row]:\n            return row\n        elif rows[c[4]][row][0] < rows[c[4]][res][0]:\n            res = row\n    return res\n\n\ndef MarkCommentRow(rows, c, row):\n    try:\n        for i in range(row, row+math.ceil(c[7])):\n            rows[c[4]][i] = c\n    except IndexError:\n        pass\n\n\ndef WriteASSHead(f, width, height, fontface, fontsize, alpha, styleid):\n    f.write(\n'''\n[Script Info]\n; Script generated by Danmaku2ASS\n; https://github.com/m13253/danmaku2ass\nScript Updated By: Danmaku2ASS (https://github.com/m13253/danmaku2ass)\nScriptType: v4.00+\nWrapStyle: 2\nCollisions: Normal\nPlayResX: %(width)s\nPlayResY: %(height)s\nScaledBorderAndShadow: yes\n[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\nStyle: %(styleid)s, %(fontface)s, %(fontsize)s, &H%(alpha)02XFFFFFF, &H%(alpha)02XFFFFFF, &H%(alpha)02X000000, &H%(alpha)02X000000, 0, 0, 0, 0, 100, 100, 0.00, 0.00, 1, %(outline)s, 0, 7, 0, 0, 0, 0\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n''' % {'width': width, 'height': height, 'fontface': fontface, 'fontsize': round(fontsize), 'alpha': 255-round(alpha*255), 'outline': round(fontsize/25), 'styleid': styleid}\n    )\n\n\ndef WriteComment(f, c, row, width, height, bottomReserved, fontsize, lifetime, styleid):\n    text = ASSEscape(c[3])\n    styles = []\n    if c[4] == 1:\n        styles.append('\\\\an8\\\\pos(%(halfwidth)s, %(row)s)' % {'halfwidth': round(width/2), 'row': row})\n    elif c[4] == 2:\n        styles.append('\\\\an2\\\\pos(%(halfwidth)s, %(row)s)' % {'halfwidth': round(width/2), 'row': ConvertType2(row, height, bottomReserved)})\n    elif c[4] == 3:\n        styles.append('\\\\move(%(neglen)s, %(row)s, %(width)s, %(row)s)' % {'width': width, 'row': row, 'neglen': -math.ceil(c[8])})\n    else:\n        styles.append('\\\\move(%(width)s, %(row)s, %(neglen)s, %(row)s)' % {'width': width, 'row': row, 'neglen': -math.ceil(c[8])})\n    if not (-1 < c[6]-fontsize < 1):\n        styles.append('\\\\fs%s' % round(c[6]))\n    if c[5] != 0xffffff:\n        styles.append('\\\\c&H%02X%02X%02X&' % (c[5] & 0xff, (c[5] >> 8) & 0xff, (c[5] >> 16) & 0xff))\n        if c[5] == 0x000000:\n            styles.append('\\\\3c&HFFFFFF&')\n    f.write('Dialogue: 2,%(start)s,%(end)s,%(styleid)s,,0000,0000,0000,,{%(styles)s}%(text)s\\n' % {'start': ConvertTimestamp(c[0]), 'end': ConvertTimestamp(c[0]+lifetime), 'styles': ''.join(styles), 'text': text, 'styleid': styleid})\n\n\ndef ASSEscape(s):\n    return '\\\\N'.join((i or ' ' for i in str(s).replace('\\\\', '\\\\\\\\').replace('{', '\\\\{').replace('}', '\\\\}').split('\\n')))\n\n\ndef CalculateLength(s):\n    return max(map(len, s.split('\\n')))  # May not be accurate\n\n\ndef ConvertTimestamp(timestamp):\n    timestamp = round(timestamp*100.0)\n    hour, minute = divmod(timestamp, 360000)\n    minute, second = divmod(minute, 6000)\n    second, centsecond = divmod(second, 100)\n    return '%d:%02d:%02d.%02d' % (int(hour), int(minute), int(second), int(centsecond))\n\n\ndef ConvertType2(row, height, bottomReserved):\n    return height-bottomReserved-row\n\n\ndef ConvertToFile(filename_or_file, *args, **kwargs):\n    if isinstance(filename_or_file, bytes):\n        filename_or_file = str(bytes(filename_or_file).decode('utf-8', 'replace'))\n    if isinstance(filename_or_file, str):\n        return open(filename_or_file, *args, **kwargs)\n    else:\n        return filename_or_file\n\n\ndef FilterBadChars(f):\n    s = f.read()\n    s = re.sub('[\\\\x00-\\\\x08\\\\x0b\\\\x0c\\\\x0e-\\\\x1f]', '\\ufffd', s)\n    return io.StringIO(s)\n\n\nclass safe_list(list):\n    def get(self, index, default=None):\n        try:\n            return self[index]\n        except IndexError:\n            return default\n\n\ndef export(func):\n    global __all__\n    try:\n        __all__.append(func.__name__)\n    except NameError:\n        __all__ = [func.__name__]\n    return func\n\n\n@export\ndef Danmaku2ASS(input_files, output_file, stage_width, stage_height, reserve_blank=0, font_face=_('(FONT) sans-serif')[7:], font_size=25.0, text_opacity=1.0, comment_duration=5.0, is_reduce_comments=False, progress_callback=None):\n    fo = None\n    comments = ReadComments(input_files, font_size)\n    try:\n        if output_file:\n            fo = ConvertToFile(output_file, 'w', encoding='utf-8-sig', errors='replace', newline='\\r\\n')\n        else:\n            fo = sys.stdout\n        ProcessComments(comments, fo, stage_width, stage_height, reserve_blank, font_face, font_size, text_opacity, comment_duration, is_reduce_comments, progress_callback)\n    finally:\n        if output_file and fo != output_file:\n            fo.close()\n\n\n@export\ndef ReadComments(input_files, font_size=25.0, progress_callback=None):\n    if isinstance(input_files, bytes):\n        input_files = str(bytes(input_files).decode('utf-8', 'replace'))\n    if isinstance(input_files, str):\n        input_files = [input_files]\n    else:\n        input_files = list(input_files)\n    comments = []\n    for idx, i in enumerate(input_files):\n        if progress_callback:\n            progress_callback(idx, len(input_files))\n        with ConvertToFile(i, 'r', encoding='utf-8', errors='replace') as f:\n            CommentProcessor = GetCommentProcessor(f)\n            if not CommentProcessor:\n                raise ValueError(_('Unknown comment file format: %s') % i)\n            comments.extend(CommentProcessor(FilterBadChars(f), font_size))\n    if progress_callback:\n        progress_callback(len(input_files), len(input_files))\n    comments.sort()\n    return comments\n\n\n@export\ndef GetCommentProcessor(input_file):\n    return CommentFormatMap[ProbeCommentFormat(input_file)]\n\n\ndef main():\n    if len(sys.argv) == 1:\n        sys.argv.append('--help')\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-o', '--output', metavar=_('OUTPUT'), help=_('Output file'))\n    parser.add_argument('-s', '--size', metavar=_('WIDTHxHEIGHT'), required=True, help=_('Stage size in pixels'))\n    parser.add_argument('-fn', '--font', metavar=_('FONT'), help=_('Specify font face [default: %s]') % _('(FONT) sans-serif')[7:], default=_('(FONT) sans-serif')[7:])\n    parser.add_argument('-fs', '--fontsize', metavar=_('SIZE'), help=(_('Default font size [default: %s]') % 25), type=float, default=25.0)\n    parser.add_argument('-a', '--alpha', metavar=_('ALPHA'), help=_('Text opacity'), type=float, default=1.0)\n    parser.add_argument('-l', '--lifetime', metavar=_('SECONDS'), help=_('Duration of comment display [default: %s]') % 5, type=float, default=5.0)\n    parser.add_argument('-p', '--protect', metavar=_('HEIGHT'), help=_('Reserve blank on the bottom of the stage'), type=int, default=0)\n    parser.add_argument('-r', '--reduce', action='store_true', help=_('Reduce the amount of comments if stage is full'))\n    parser.add_argument('file', metavar=_('FILE'), nargs='+', help=_('Comment file to be processed'))\n    args = parser.parse_args()\n    try:\n        width, height = str(args.size).split('x', 1)\n        width = int(width)\n        height = int(height)\n    except ValueError:\n        raise ValueError(_('Invalid stage size: %r') % args.size)\n    Danmaku2ASS(args.file, args.output, width, height, args.protect, args.font, args.fontsize, args.alpha, args.lifetime, args.reduce)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "bilibili_luckyman/README.md",
    "content": "## 说明\n\nB 站 30 万粉丝抽奖，自己写了一个转发抽奖助手。\n\n上次活动：\n\nhttps://t.bilibili.com/675922191916728342\n"
  },
  {
    "path": "bilibili_luckyman/bilibili_luckyman.py",
    "content": "# -*- coding:utf-8 -*-\nimport requests\nimport json\nimport re\nimport random\nimport time\n\ndef get_dynamic_id(url):\n    dynamic_id = re.findall(r'\\d+', url)\n    return dynamic_id\n\ndef get_data(detail_url, params):\n    req = requests.get(url = detail_url, params = params)\n    req_text = json.loads(req.text)\n    data = req_text['data']\n    offset = data['offset']\n    items = data['items']\n    return offset, items\n\ndef get_uses(dynamic_id):\n    detail_url = \"https://api.bilibili.com/x/polymer/web-dynamic/v1/detail/forward\"\n    params = {'id': dynamic_id}\n\n    offset, items = get_data(detail_url, params)\n\n    all_user_name = []\n    all_user_text = []\n    all_user_mid = []\n\n    while offset != \"\":\n        for item in items:\n            name = item['user']['name']\n            all_user_name.append(name)\n            mid = item['user']['mid']\n            all_user_mid.append(mid)\n            text = item['desc']['text']\n            all_user_text.append(text)\n\n        params = {\n            'id': dynamic_id,\n            'offset': offset\n        }\n        offset, items = get_data(detail_url, params)\n\n    return all_user_name, all_user_mid, all_user_text\n\ndef get_lucky_man(num, lucky_num):\n\n    tmp = [i for i in range(0, num)]\n    random.shuffle(tmp)\n    top30_shuffle_id = tmp[:lucky_num]\n    return top30_shuffle_id\n\ndef get_local_time():\n    localtime = \"[\" + str(time.strftime('%H:%M:%S',time.localtime(time.time()))) + \"]\"\n    return localtime\n\nif __name__ == \"__main__\":\n    print (\"+----------------------------------------+\")\n    print (\"      |动态转发抽奖助手 by Jack Cui|\")\n    print (\"+----------------------------------------+\")\n    # 动态链接，修改为你自己的动态\n    url = \"https://t.bilibili.com/675922191916728342\"\n    print (get_local_time() + \" 正在获取转发数据中......\")\n\n    awards = [\n        \"动手深度学习\",\n        \"机器学习公式详解\",\n        \"Easy RL 强化学习教程\",\n        \"数学之美\",\n        \"浪潮之巅 第四版\",\n        \"C Primer Plus（第6版）中文版\"\n    ] * 5\n\n    # 设置随机数种子，保证随机数固定，这里种子数设为转发数+评论数+点赞数\n    random.seed(1462 + 213 + 399)\n    random.shuffle(awards)\n\n    dynamic_id = get_dynamic_id(url)\n    all_user_name, all_user_mid, all_user_text = get_uses(dynamic_id)\n\n    top30_shuffle_id = get_lucky_man(len(all_user_name), 30)\n    print (get_local_time() + \" 中奖用户信息：\\n\")\n    for idx, id_ in enumerate(top30_shuffle_id):\n        print(\"用户名：{}\".format(all_user_name[id_]))\n        print(\"用户主页：{}\".format(\"https://space.bilibili.com/\" + str(all_user_mid[id_])))\n        print(\"转发内容：{}\".format(all_user_text[id_]))\n        print(\"获得奖品：{}\".format(awards[idx]))\n        print(\"*\" * 50)\n"
  },
  {
    "path": "biqukan.py",
    "content": "# -*- coding:UTF-8 -*-\nfrom urllib import request\nfrom bs4 import BeautifulSoup\nimport collections\nimport re\nimport os\nimport time\nimport sys\nimport types\n\n\"\"\"\n类说明:下载《笔趣看》网小说: url:https://www.biqukan.com/\n\nParameters:\n\ttarget - 《笔趣看》网指定的小说目录地址(string)\n\nReturns:\n\t无\n\nModify:\n\t2017-05-06\n\"\"\"\nclass download(object):\n\tdef __init__(self, target):\n\t\tself.__target_url = target\n\t\tself.__head = {'User-Agent':'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19',}\n\n\t\"\"\"\n\t函数说明:获取下载链接\n\n\tParameters:\n\t\t无\n\n\tReturns:\n\t\tnovel_name + '.txt' - 保存的小说名(string)\n\t\tnumbers - 章节数(int)\n\t\tdownload_dict - 保存章节名称和下载链接的字典(dict)\n\n\tModify:\n\t\t2017-05-06\n\t\"\"\"\n\tdef get_download_url(self):\n\t\tcharter = re.compile(u'[第弟](.+)章', re.IGNORECASE)\n\t\ttarget_req = request.Request(url = self.__target_url, headers = self.__head)\n\t\ttarget_response = request.urlopen(target_req)\n\t\ttarget_html = target_response.read().decode('gbk','ignore')\n\t\tlistmain_soup = BeautifulSoup(target_html,'lxml')\n\t\tchapters = listmain_soup.find_all('div',class_ = 'listmain')\n\t\tdownload_soup = BeautifulSoup(str(chapters), 'lxml')\n\t\tnovel_name = str(download_soup.dl.dt).split(\"》\")[0][5:]\n\t\tflag_name = \"《\" + novel_name + \"》\" + \"正文卷\"\n\t\tnumbers = (len(download_soup.dl.contents) - 1) / 2 - 8\n\t\tdownload_dict = collections.OrderedDict()\n\t\tbegin_flag = False\n\t\tnumbers = 1\n\t\tfor child in download_soup.dl.children:\n\t\t\tif child != '\\n':\n\t\t\t\tif child.string == u\"%s\" % flag_name:\n\t\t\t\t\tbegin_flag = True\n\t\t\t\tif begin_flag == True and child.a != None:\n\t\t\t\t\tdownload_url = \"https://www.biqukan.com\" + child.a.get('href')\n\t\t\t\t\tdownload_name = child.string\n\t\t\t\t\tnames = str(download_name).split('章')\n\t\t\t\t\tname = charter.findall(names[0] + '章')\n\t\t\t\t\tif name:\n\t\t\t\t\t\t\tdownload_dict['第' + str(numbers) + '章 ' + names[1]] = download_url\n\t\t\t\t\t\t\tnumbers += 1\n\t\treturn novel_name + '.txt', numbers, download_dict\n\t\n\t\"\"\"\n\t函数说明:爬取文章内容\n\n\tParameters:\n\t\turl - 下载连接(string)\n\n\tReturns:\n\t\tsoup_text - 章节内容(string)\n\n\tModify:\n\t\t2017-05-06\n\t\"\"\"\n\tdef Downloader(self, url):\n\t\tdownload_req = request.Request(url = url, headers = self.__head)\n\t\tdownload_response = request.urlopen(download_req)\n\t\tdownload_html = download_response.read().decode('gbk','ignore')\n\t\tsoup_texts = BeautifulSoup(download_html, 'lxml')\n\t\ttexts = soup_texts.find_all(id = 'content', class_ = 'showtxt')\n\t\tsoup_text = BeautifulSoup(str(texts), 'lxml').div.text.replace('\\xa0','')\n\t\treturn soup_text\n\n\t\"\"\"\n\t函数说明:将爬取的文章内容写入文件\n\n\tParameters:\n\t\tname - 章节名称(string)\n\t\tpath - 当前路径下,小说保存名称(string)\n\t\ttext - 章节内容(string)\n\n\tReturns:\n\t\t无\n\n\tModify:\n\t\t2017-05-06\n\t\"\"\"\n\tdef Writer(self, name, path, text):\n\t\twrite_flag = True\n\t\twith open(path, 'a', encoding='utf-8') as f:\n\t\t\tf.write(name + '\\n\\n')\n\t\t\tfor each in text:\n\t\t\t\tif each == 'h':\n\t\t\t\t\twrite_flag = False\n\t\t\t\tif write_flag == True and each != ' ':\n\t\t\t\t\tf.write(each)\n\t\t\t\tif write_flag == True and each == '\\r':\n\t\t\t\t\tf.write('\\n')\t\t\t\n\t\t\tf.write('\\n\\n')\n\nif __name__ == \"__main__\":\n\tprint(\"\\n\\t\\t欢迎使用《笔趣看》小说下载小工具\\n\\n\\t\\t作者:Jack-Cui\\t时间:2017-05-06\\n\")\n\tprint(\"*************************************************************************\")\n\t\n\t#小说地址\n\ttarget_url = str(input(\"请输入小说目录下载地址:\\n\"))\n\n\t#实例化下载类\n\td = download(target = target_url)\n\tname, numbers, url_dict = d.get_download_url()\n\tif name in os.listdir():\n\t\tos.remove(name)\n\tindex = 1\n\n\t#下载中\n\tprint(\"《%s》下载中:\" % name[:-4])\n\tfor key, value in url_dict.items():\n\t\td.Writer(key, name, d.Downloader(value))\n\t\tsys.stdout.write(\"已下载:%.3f%%\" %  float(index/numbers) + '\\r')\n\t\tsys.stdout.flush()\n\t\tindex += 1\t\n\n\tprint(\"《%s》下载完成！\" % name[:-4])\n\n\t\n"
  },
  {
    "path": "cartoon/cartoon/__init__.py",
    "content": ""
  },
  {
    "path": "cartoon/cartoon/items.py",
    "content": "# -*- coding: utf-8 -*-\n\n# Define here the models for your scraped items\n#\n# See documentation in:\n# http://doc.scrapy.org/en/latest/topics/items.html\n\nimport scrapy\n\nclass ComicItem(scrapy.Item):\n\tdir_name = scrapy.Field()\n\tlink_url = scrapy.Field()\n\timg_url = scrapy.Field()\n\timage_paths = scrapy.Field()"
  },
  {
    "path": "cartoon/cartoon/middlewares.py",
    "content": "# -*- coding: utf-8 -*-\n\n# Define here the models for your spider middleware\n#\n# See documentation in:\n# http://doc.scrapy.org/en/latest/topics/spider-middleware.html\n\nfrom scrapy import signals\n\n\nclass CartoonSpiderMiddleware(object):\n    # Not all methods need to be defined. If a method is not defined,\n    # scrapy acts as if the spider middleware does not modify the\n    # passed objects.\n\n    @classmethod\n    def from_crawler(cls, crawler):\n        # This method is used by Scrapy to create your spiders.\n        s = cls()\n        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)\n        return s\n\n    def process_spider_input(response, spider):\n        # Called for each response that goes through the spider\n        # middleware and into the spider.\n\n        # Should return None or raise an exception.\n        return None\n\n    def process_spider_output(response, result, spider):\n        # Called with the results returned from the Spider, after\n        # it has processed the response.\n\n        # Must return an iterable of Request, dict or Item objects.\n        for i in result:\n            yield i\n\n    def process_spider_exception(response, exception, spider):\n        # Called when a spider or process_spider_input() method\n        # (from other spider middleware) raises an exception.\n\n        # Should return either None or an iterable of Response, dict\n        # or Item objects.\n        pass\n\n    def process_start_requests(start_requests, spider):\n        # Called with the start requests of the spider, and works\n        # similarly to the process_spider_output() method, except\n        # that it doesn’t have a response associated.\n\n        # Must return only requests (not items).\n        for r in start_requests:\n            yield r\n\n    def spider_opened(self, spider):\n        spider.logger.info('Spider opened: %s' % spider.name)\n"
  },
  {
    "path": "cartoon/cartoon/pipelines.py",
    "content": "# -*- coding: utf-8 -*-\n\n# Define your item pipelines here\n#\n# Don't forget to add your pipeline to the ITEM_PIPELINES setting\n# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html\nfrom cartoon import settings\nfrom scrapy import Request\nimport requests\nimport os\n\n\nclass ComicImgDownloadPipeline(object):\n\n\tdef process_item(self, item, spider):\n\t\t#如果获取了图片链接，进行如下操作\n\t\tif 'img_url' in item:\n\t\t\timages = []\n\t\t\t#文件夹名字\n\t\t\tdir_path = '%s/%s' % (settings.IMAGES_STORE, item['dir_name'])\n\t\t\t#文件夹不存在则创建文件夹\n\t\t\tif not os.path.exists(dir_path):\n\t\t\t\tos.makedirs(dir_path)\n\t\t\t#获取每一个图片链接\n\t\t\tfor image_url in item['img_url']:\n\t\t\t\t#解析链接，根据链接为图片命名\n\t\t\t\thouzhui = image_url.split('/')[-1].split('.')[-1]\n\t\t\t\tqianzhui = item['link_url'].split('/')[-1].split('.')[0]\n\t\t\t\t#图片名\n\t\t\t\timage_file_name = '第' + qianzhui + '页.' + houzhui\n\t\t\t\t#图片保存路径\n\t\t\t\tfile_path = '%s/%s' % (dir_path, image_file_name)\n\t\t\t\timages.append(file_path)\n\t\t\t\tif os.path.exists(file_path):\n\t\t\t\t\tcontinue\n\t\t\t\t#保存图片\n\t\t\t\twith open(file_path, 'wb') as handle:\n\t\t\t\t\tresponse = requests.get(url = image_url)\n\t\t\t\t\tfor block in response.iter_content(1024):\n\t\t\t\t\t\tif not block:\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\thandle.write(block)\n\t\t\t#返回图片保存路径\n\t\t\titem['image_paths'] = images\n\t\treturn item"
  },
  {
    "path": "cartoon/cartoon/settings.py",
    "content": "# -*- coding: utf-8 -*-\n\n# Scrapy settings for cartoon project\n#\n# For simplicity, this file contains only settings considered important or\n# commonly used. You can find more settings consulting the documentation:\n#\n#     http://doc.scrapy.org/en/latest/topics/settings.html\n#     http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html\n#     http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html\n\nBOT_NAME = 'cartoon'\n\nSPIDER_MODULES = ['cartoon.spiders']\nNEWSPIDER_MODULE = 'cartoon.spiders'\n\n\n# Crawl responsibly by identifying yourself (and your website) on the user-agent\n#USER_AGENT = 'cartoon (+http://www.yourdomain.com)'\n\n# Obey robots.txt rules\nROBOTSTXT_OBEY = False\n\nITEM_PIPELINES = {\n\t'cartoon.pipelines.ComicImgDownloadPipeline': 1,\n}\n\nIMAGES_STORE = 'H:/火影忍者'\n\nCOOKIES_ENABLED = False\n\nDOWNLOAD_DELAY = 0.25    # 250 ms of delay\n\n# Configure maximum concurrent requests performed by Scrapy (default: 16)\n#CONCURRENT_REQUESTS = 32\n\n# Configure a delay for requests for the same website (default: 0)\n# See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay\n# See also autothrottle settings and docs\n#DOWNLOAD_DELAY = 3\n# The download delay setting will honor only one of:\n#CONCURRENT_REQUESTS_PER_DOMAIN = 16\n#CONCURRENT_REQUESTS_PER_IP = 16\n\n# Disable cookies (enabled by default)\n#COOKIES_ENABLED = False\n\n# Disable Telnet Console (enabled by default)\n#TELNETCONSOLE_ENABLED = False\n\n# Override the default request headers:\n#DEFAULT_REQUEST_HEADERS = {\n#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n#   'Accept-Language': 'en',\n#}\n\n# Enable or disable spider middlewares\n# See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html\n#SPIDER_MIDDLEWARES = {\n#    'cartoon.middlewares.CartoonSpiderMiddleware': 543,\n#}\n\n# Enable or disable downloader middlewares\n# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html\n#DOWNLOADER_MIDDLEWARES = {\n#    'cartoon.middlewares.MyCustomDownloaderMiddleware': 543,\n#}\n\n# Enable or disable extensions\n# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html\n#EXTENSIONS = {\n#    'scrapy.extensions.telnet.TelnetConsole': None,\n#}\n\n# Configure item pipelines\n# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html\n#ITEM_PIPELINES = {\n#    'cartoon.pipelines.CartoonPipeline': 300,\n#}\n\n# Enable and configure the AutoThrottle extension (disabled by default)\n# See http://doc.scrapy.org/en/latest/topics/autothrottle.html\n#AUTOTHROTTLE_ENABLED = True\n# The initial download delay\n#AUTOTHROTTLE_START_DELAY = 5\n# The maximum download delay to be set in case of high latencies\n#AUTOTHROTTLE_MAX_DELAY = 60\n# The average number of requests Scrapy should be sending in parallel to\n# each remote server\n#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0\n# Enable showing throttling stats for every response received:\n#AUTOTHROTTLE_DEBUG = False\n\n# Enable and configure HTTP caching (disabled by default)\n# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings\n#HTTPCACHE_ENABLED = True\n#HTTPCACHE_EXPIRATION_SECS = 0\n#HTTPCACHE_DIR = 'httpcache'\n#HTTPCACHE_IGNORE_HTTP_CODES = []\n#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'\n"
  },
  {
    "path": "cartoon/cartoon/spiders/__init__.py",
    "content": "# This package will contain the spiders of your Scrapy project\n#\n# Please refer to the documentation for information on how to create and manage\n# your spiders.\n"
  },
  {
    "path": "cartoon/cartoon/spiders/comic_spider.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport re\nimport scrapy\nfrom scrapy import Selector\nfrom cartoon.items import ComicItem\n\nclass ComicSpider(scrapy.Spider):\n\tname = 'comic'\n\n\tdef __init__(self):\n\t\t#图片链接server域名\n\t\tself.server_img = 'http://n.1whour.com/'\n\t\t#章节链接server域名\n\t\tself.server_link = 'http://comic.kukudm.com'\n\t\tself.allowed_domains = ['comic.kukudm.com']\n\t\tself.start_urls = ['http://comic.kukudm.com/comiclist/3/']\n\t\t#匹配图片地址的正则表达式\n\t\tself.pattern_img = re.compile(r'\\+\"(.+)\\'><span')\n\n\t#从start_requests发送请求\n\tdef start_requests(self):\n\t\tyield scrapy.Request(url = self.start_urls[0], callback = self.parse1)\n\n\t#解析response,获得章节图片链接地址\n\tdef parse1(self, response):\n\t\thxs = Selector(response)\n\t\titems = []\n\t\t#章节链接地址\n\t\turls = hxs.xpath('//dd/a[1]/@href').extract()\n\t\t#章节名\n\t\tdir_names = hxs.xpath('//dd/a[1]/text()').extract()\n\t\t#保存章节链接和章节名\n\t\tfor index in range(len(urls)):\n\t\t\titem = ComicItem()\n\t\t\titem['link_url'] = self.server_link + urls[index]\n\t\t\titem['dir_name'] = dir_names[index]\n\t\t\titems.append(item)\n\n\t\t#根据每个章节的链接，发送Request请求，并传递item参数\n\t\tfor item in items:\n\t\t\tyield scrapy.Request(url = item['link_url'], meta = {'item':item}, callback = self.parse2)\n\t\t\n\t#解析获得章节第一页的页码数和图片链接\t\n\tdef parse2(self, response):\n\t\t#接收传递的item\n\t\titem = response.meta['item']\n\t\t#获取章节的第一页的链接\n\t\titem['link_url'] = response.url\n\t\thxs = Selector(response)\n\t\t#获取章节的第一页的图片链接\n\t\tpre_img_url = hxs.xpath('//script/text()').extract()\n\t\t#注意这里返回的图片地址,应该为列表,否则会报错\n\t\timg_url = [self.server_img + re.findall(self.pattern_img, pre_img_url[0])[0]]\n\t\t#将获取的章节的第一页的图片链接保存到img_url中\n\t\titem['img_url'] = img_url\n\t\t#返回item，交给item pipeline下载图片\n\t\tyield item\n\t\t#获取章节的页数\n\t\tpage_num = hxs.xpath('//td[@valign=\"top\"]/text()').re(u'共(\\d+)页')[0]\n\t\t#根据页数，整理出本章节其他页码的链接\n\t\tpre_link = item['link_url'][:-5]\n\t\tfor each_link in range(2, int(page_num) + 1):\n\t\t\tnew_link = pre_link + str(each_link) + '.htm'\n\t\t\t#根据本章节其他页码的链接发送Request请求，用于解析其他页码的图片链接，并传递item\n\t\t\tyield scrapy.Request(url = new_link, meta = {'item':item}, callback = self.parse3)\n\n\t#解析获得本章节其他页面的图片链接\n\tdef parse3(self, response):\n\t\t#接收传递的item\n\t\titem = response.meta['item']\n\t\t#获取该页面的链接\n\t\titem['link_url'] = response.url\n\t\thxs = Selector(response)\n\t\tpre_img_url = hxs.xpath('//script/text()').extract()\n\t\t#注意这里返回的图片地址,应该为列表,否则会报错\n\t\timg_url = [self.server_img + re.findall(self.pattern_img, pre_img_url[0])[0]]\n\t\t#将获取的图片链接保存到img_url中\n\t\titem['img_url'] = img_url\n\t\t#返回item，交给item pipeline下载图片\n\t\tyield item\n\t\t"
  },
  {
    "path": "cartoon/scrapy.cfg",
    "content": "# Automatically created by: scrapy startproject\n#\n# For more information about the [deploy] section see:\n# https://scrapyd.readthedocs.org/en/latest/deploy.html\n\n[settings]\ndefault = cartoon.settings\n\n[deploy]\n#url = http://localhost:6800/\nproject = cartoon\n"
  },
  {
    "path": "daili.py",
    "content": "# -*- coding:UTF-8 -*-\nfrom bs4 import BeautifulSoup\nfrom selenium import webdriver\nimport subprocess as sp\nfrom lxml import etree\nimport requests\nimport random\nimport re\n\n\"\"\"\n函数说明:获取IP代理\nParameters:\n\tpage - 高匿代理页数,默认获取第一页\nReturns:\n\tproxys_list - 代理列表\nModify:\n\t2017-05-27\n\"\"\"\ndef get_proxys(page = 1):\n\t#requests的Session可以自动保持cookie,不需要自己维护cookie内容\n\tS = requests.Session()\n\t#西祠代理高匿IP地址\n\ttarget_url = 'http://www.xicidaili.com/nn/%d' % page\n\t#完善的headers\n\ttarget_headers = {'Upgrade-Insecure-Requests':'1',\n\t\t'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',\n\t\t'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',\n\t\t'Referer':'http://www.xicidaili.com/nn/',\n\t\t'Accept-Encoding':'gzip, deflate, sdch',\n\t\t'Accept-Language':'zh-CN,zh;q=0.8',\n\t}\n\t#get请求\n\ttarget_response = S.get(url = target_url, headers = target_headers)\n\t#utf-8编码\n\ttarget_response.encoding = 'utf-8'\n\t#获取网页信息\n\ttarget_html = target_response.text\n\t#获取id为ip_list的table\n\tbf1_ip_list = BeautifulSoup(target_html, 'lxml')\n\tbf2_ip_list = BeautifulSoup(str(bf1_ip_list.find_all(id = 'ip_list')), 'lxml')\n\tip_list_info = bf2_ip_list.table.contents\n\t#存储代理的列表\n\tproxys_list = []\n\t#爬取每个代理信息\n\tfor index in range(len(ip_list_info)):\n\t\tif index % 2 == 1 and index != 1:\n\t\t\tdom = etree.HTML(str(ip_list_info[index]))\n\t\t\tip = dom.xpath('//td[2]')\n\t\t\tport = dom.xpath('//td[3]')\n\t\t\tprotocol = dom.xpath('//td[6]')\n\t\t\tproxys_list.append(protocol[0].text.lower() + '#' + ip[0].text + '#' + port[0].text)\n\t#返回代理列表\n\treturn proxys_list\n\n\"\"\"\n函数说明:检查代理IP的连通性\nParameters:\n\tip - 代理的ip地址\n\tlose_time - 匹配丢包数\n\twaste_time - 匹配平均时间\nReturns:\n\taverage_time - 代理ip平均耗时\nModify:\n\t2017-05-27\n\"\"\"\ndef check_ip(ip, lose_time, waste_time):\n\t#命令 -n 要发送的回显请求数 -w 等待每次回复的超时时间(毫秒)\n\tcmd = \"ping -n 3 -w 3 %s\"\n\t#执行命令\n\tp = sp.Popen(cmd % ip, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE, shell=True) \n\t#获得返回结果并解码\n\tout = p.stdout.read().decode(\"gbk\")\n\t#丢包数\n\tlose_time = lose_time.findall(out)\n\t#当匹配到丢失包信息失败,默认为三次请求全部丢包,丢包数lose赋值为3\n\tif len(lose_time) == 0:\n\t\tlose = 3\n\telse:\n\t\tlose = int(lose_time[0])\n\t#如果丢包数目大于2个,则认为连接超时,返回平均耗时1000ms\n\tif lose > 2:\n\t\t#返回False\n\t\treturn 1000\n\t#如果丢包数目小于等于2个,获取平均耗时的时间\n\telse:\n\t\t#平均时间\n\t\taverage = waste_time.findall(out)\n\t\t#当匹配耗时时间信息失败,默认三次请求严重超时,返回平均好使1000ms\n\t\tif len(average) == 0:\n\t\t\treturn 1000\n\t\telse:\n\t\t\t#\n\t\t\taverage_time = int(average[0])\n\t\t\t#返回平均耗时\n\t\t\treturn average_time\n\n\"\"\"\n函数说明:初始化正则表达式\nParameters:\n\t无\nReturns:\n\tlose_time - 匹配丢包数\n\twaste_time - 匹配平均时间\nModify:\n\t2017-05-27\n\"\"\"\ndef initpattern():\n\t#匹配丢包数\n\tlose_time = re.compile(u\"丢失 = (\\d+)\", re.IGNORECASE)\n\t#匹配平均时间\n\twaste_time = re.compile(u\"平均 = (\\d+)ms\", re.IGNORECASE)\n\treturn lose_time, waste_time\n\nif __name__ == '__main__':\n\t#初始化正则表达式\n\tlose_time, waste_time = initpattern()\n\t#获取IP代理\n\tproxys_list = get_proxys(1)\n\n\t#如果平均时间超过200ms重新选取ip\n\twhile True:\n\t\t#从100个IP中随机选取一个IP作为代理进行访问\n\t\tproxy = random.choice(proxys_list)\n\t\tsplit_proxy = proxy.split('#')\n\t\t#获取IP\n\t\tip = split_proxy[1]\n\t\t#检查ip\n\t\taverage_time = check_ip(ip, lose_time, waste_time)\n\t\tif average_time > 200:\n\t\t\t#去掉不能使用的IP\n\t\t\tproxys_list.remove(proxy)\n\t\t\tprint(\"ip连接超时, 重新获取中!\")\n\t\tif average_time < 200:\n\t\t\tbreak\n\n\t#去掉已经使用的IP\n\tproxys_list.remove(proxy)\n\tproxy_dict = {split_proxy[0]:split_proxy[1] + ':' + split_proxy[2]}\n\tprint(\"使用代理:\", proxy_dict)\n"
  },
  {
    "path": "dingdong/README.md",
    "content": "## 功能\n\n下载京东商品的晒单图。\n\n## 作者\n\n* Website: [http://cuijiahua.com](http://cuijiahua.com \"悬停显示\")\n* Author: Jack Cui\n* Date: 2018.7.7\n\n## 效果图：\n\n![image](https://github.com/Jack-Cherish/Pictures/blob/master/jd.gif)\n\n## 使用说明\n\n\tpython jd.py -k 芒果\n\n\t三个参数：\n\t-d\t保存图片的路径，默认为fd.py文件所在文件夹\n\t-k\t搜索关键词\n\t-n  \t下载商品的晒单图个数，即n个商店的晒单图\n"
  },
  {
    "path": "dingdong/jd.py",
    "content": "# -*-coding:utf-8 -*-\n# Author:Jack Cui\n# Website:http://cuijiahua.com\n# Date:2018-7-7\nimport os\nimport re\nimport sys\nimport bs4\nimport json\nimport math\nimport time\nimport math\nimport argparse\nimport requests\nfrom contextlib import closing\n\ndef search_goods(keyword, pages):\n\t\"\"\"\n\t搜索商品\n\tParameters:\n\t\tkeyword - str 搜索关键词\n\t\tpages - int 搜索页数\n\tReturns:\n\t\tgoods_urls - list 商品链接\n\t\"\"\"\n\t# 创建session\n\tsess = requests.Session()\n\tgoods_urls = []\n\tfor page in range(pages):\n\t\t# 第一次加载\n\t\tsearch_headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',\n\t\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.9',\n\t\t\t'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n\t\t\t'Host': 'search.jd.com'}\n\t\ts = page*28\n\t\tif s == 0:\n\t\t\ts = 1\n\t\t# 搜索url\n\t\tsearch_url = 'https://search.jd.com/Search'\n\t\tsearch_params = {'keyword':keyword,\n\t\t\t'enc':'utf-8',\n\t\t\t'qrst':'1',\n\t\t\t'rt':'1',\n\t\t\t'stop':'1',\n\t\t\t'vt':'2',\n\t\t\t'wq':keyword,\n\t\t\t'stock':'1',\n\t\t\t'page':page*2+1,\n\t\t\t's':s,\n\t\t\t'click':'0'}\n\t\tsearch_req = sess.get(url=search_url, params=search_params, headers=search_headers, verify=False)\n\t\tsearch_req.encoding = 'utf-8'\n\t\t# 匹配商品链接\n\t\tsearch_req_bf = bs4.BeautifulSoup(search_req.text, 'lxml')\n\t\tfor item in search_req_bf.find_all('li', class_='gl-item'):\n\t\t\titem_url = item.div.div.a.get('href')\n\t\t\t# 滤除广告\n\t\t\tif 'ccc-x.jd.com' not in item_url:\n\t\t\t\tgoods_urls.append(item_url)\n\t\t# 继续加载log_id\n\t\tlog_id = re.findall(\"log_id:'(.*)',\", search_req.text)[0]\n\t\t\n\t\t# 第二次加载\n\t\t# 继续加载url\n\t\tsearch_more_url = 'https://search.jd.com/s_new.php'\n\t\tsearch_more_headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',\n\t\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.9',\n\t\t\t'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n\t\t\t'Host': 'search.jd.com',\n\t\t\t'Referer':search_req.url}\n\t\ts = (1+page)*25\n\t\tsearch_more_params = {'keyword':keyword,\n\t\t\t'enc':'utf-8',\n\t\t\t'qrst':'1',\n\t\t\t'rt':'1',\n\t\t\t'stop':'1',\n\t\t\t'vt':'2',\n\t\t\t'wq':keyword,\n\t\t\t'stock':'1',\n\t\t\t'page':(1+page)*2,\n\t\t\t's':s,\n\t\t\t'log_id':log_id,\n\t\t\t'scrolling':'y',\n\t\t\t'tpl':'1_M'}\n\t\tsearch_more_req = sess.get(url=search_more_url, params=search_more_params, headers=search_more_headers, verify=False)\n\t\tsearch_more_req.encoding = 'utf-8'\n\t\t# 匹配商品链接\n\t\tsearch_more_req_bf = bs4.BeautifulSoup(search_more_req.text, 'lxml')\n\t\tfor item in search_more_req_bf.find_all('li', class_='gl-item'):\n\t\t\titem_url = item.div.div.a.get('href')\n\t\t\t# 滤除广告\n\t\t\tif 'ccc-x.jd.com' not in item_url:\n\t\t\t\tgoods_urls.append(item_url)\n\t# 去重\n\tgoods_urls = list(set(goods_urls))\n\t# 链接合成\n\tgoods_urls = list(map(lambda x: 'http:'+x, goods_urls))\n\treturn goods_urls\n\ndef goods_images(goods_url):\n\t\"\"\"\n\t获得商品晒图\n\tParameters:\n\t\tgoods_url - str 商品链接\n\tReturns:\n\t\timage_urls - list 图片链接\n\t\"\"\"\n\timage_urls = []\n\tproductId = goods_url.split('/')[-1].split('.')[0]\n\n\t# 评论url\n\tcomment_url = 'https://sclub.jd.com/comment/productPageComments.action'\n\tcomment_params = {'productId':productId,\n\t\t'score':'0',\n\t\t'sortType':'5',\n\t\t'page':'0',\n\t\t'pageSize':'10',\n\t\t'isShadowSku':'0',\n\t\t'fold':'1'}\n\tcomment_headers = {'Accept': '*/*',\n\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t'Accept-Language': 'zh-CN,zh;q=0.9',\n\t\t'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',\n\t\t'Referer':goods_url,\n\t\t'Host': 'sclub.jd.com'}\n\n\tcomment_req = requests.get(url=comment_url, params=comment_params, headers=comment_headers, verify=False)\n\thtml = json.loads(comment_req.text)\n\t# 获得晒图个数\n\timageListCount = html['imageListCount']\n\t# 计算晒图页数,向上取整\n\tpages = math.ceil(imageListCount / 10)\n\tfor page in range(1, pages+1):\n\t\t# 获取晒图图片url\n\t\tclub_url = 'https://club.jd.com/discussion/getProductPageImageCommentList.action'\n\t\tnow = time.time()\n\t\tnow_str = str(now).split('.')\n\t\tnow = now_str[0] + now_str[-1][:3]\n\t\tclub_params = {'productId':productId,\n\t\t\t'isShadowSku':'0',\n\t\t\t'page':page,\n\t\t\t'pageSize':'10',\n\t\t\t'_':now}\n\t\tclub_headers = comment_headers\n\t\tclub_req = requests.get(url=club_url, params=club_params, headers=club_headers, verify=False)\n\t\thtml = json.loads(club_req.text)\n\t\tfor img in html['imgComments']['imgList']:\n\t\t\timage_urls.append(img['imageUrl'])\n\t# 去重\n\timage_urls = list(set(image_urls))\n\t# 链接合成\n\timage_urls = list(map(lambda x: 'http:'+x, image_urls))\n\n\treturn image_urls\n\ndef download_image(path, image_url):\n\t\"\"\"\n\t图片下载\n\tParameters:\n\t\tpath - str 图片保存地址\n\t\timage_url - str 图片下载地址\n\tReturns:\n\t\tNone\n\t\"\"\"\n\tprint(image_url)\n\tfilename = image_url.split('/')[-1]\n\timage_path = os.path.join(path, filename)\n\tdownload_headers = {'Accept': '*/*',\n\t\t'Accept-Encoding': 'gzip, deflate, br',\n\t\t'Accept-Language': 'zh-CN,zh;q=0.9',\n\t\t'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36'}\n\tsize = 0\n\twith closing(requests.get(image_url, headers=download_headers, stream=True)) as response:\n\t\tchunk_size = 1024\n\t\tcontent_size = int(response.headers['content-length'])\n\t\tif response.status_code == 200:\n\t\t\tsys.stdout.write(filename+'下载中:\\n')\n\t\t\tsys.stdout.write('    [文件大小]:%0.2f MB\\n' % (content_size / chunk_size / 1024))\n\n\t\t\twith open(image_path, 'wb') as file:\n\t\t\t\tfor data in response.iter_content(chunk_size = chunk_size):\n\t\t\t\t\tfile.write(data)\n\t\t\t\t\tsize += len(data)\n\t\t\t\t\tfile.flush()\n\t\t\t\t\tsys.stdout.write('    [下载进度]:%.2f%%' % float(size / content_size * 100) + '\\r')\n\t\t\t\t\tsys.stdout.flush()\n\ndef run(path, keyword, num):\n\t\"\"\"\n\t运行函数\n\tParameters:\n\t\tpath - str 图片保存目录\n\t\tkeyword - str 关键词\n\t\tnum - int 下载的商店个数\n\tReturns:\n\t\tNone\n\t\"\"\"\n\tflag = False\n\tpages = 1\n\twhile flag == False:\n\t\tgoods_urls = search_goods(keyword, pages)\n\t\tif len(goods_urls) > num:\n\t\t\tflag = True\n\t\telse:\n\t\t\tpages += 1\n\n\tif keyword not in os.listdir():\n\t\tos.mkdir(keyword)\n\tpath = os.path.join(path, keyword)\n\tfor goods_url in goods_urls[:num]:\n\t\timage_urls = goods_images(goods_url)\n\t\tfor image_url in image_urls:\n\t\t\tdownload_image(path, image_url)\n\nif __name__ == '__main__':\n\tif len(sys.argv) == 1:\n\t\tsys.argv.append('--help')\n\tparser = argparse.ArgumentParser()\n\tparser.add_argument('-d', '--dir', help=('store path'), type=str, default=os.path.dirname(__file__))\n\tparser.add_argument('-k', '--keyword', required=True, help=('search content'))\n\tparser.add_argument('-n', '--num', help=('the number of goods to download images'), type=int, default=1)\n\targs = parser.parse_args()\n\trun(args.dir, args.keyword, args.num)\n"
  },
  {
    "path": "douyin/README.md",
    "content": "## 功能\n\n下载指定用户的抖音视频。\n\n## 作者\n\n* Author: [Jack Cui](http://cuijiahua.com \"悬停显示\")、[steven7851](https://github.com/steven7851 \"悬停显示\")\n\n## 运行效果\n\n![image](https://github.com/Jack-Cherish/Pictures/blob/master/14.gif)\n\n## 使用说明\n\n\tpython douyin.py\n\n签名服务来源：https://github.com/coder-fly/douyin-signature<br />\n也可以使用 pyppeteer 模拟浏览器来取得签名，如此就不必依赖服务<br />\n要是以后服务器关了再来弄吧。 。\n"
  },
  {
    "path": "douyin/douyin.py",
    "content": "# -*- coding:utf-8 -*-\nfrom contextlib import closing\nimport requests, json, re, os, sys\nimport urllib\n\nclass DouYin(object):\n\tdef __init__(self, width = 500, height = 300):\n\t\t\"\"\"\n\t\t抖音App视频下载\n\t\t\"\"\"\n\t\tself.headers = {\n\t\t\t'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',\n\t\t\t'sec-fetch-mode': 'cors',\n\t\t\t'sec-fetch-site': 'same-origin',\n\t\t\t'accept': 'application/json',\n\t\t\t'accept-encoding': 'gzip, deflate, br',\n\t\t\t'accept-language': 'zh-CN,zh;q=0.9',\n\t\t}\n\t\tself.headers1 = {\n\t\t\t'User-Agent': 'Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; MI 4S Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.1.3',\n\t\t}\n\n\tdef get_video_urls(self, user_id, type_flag='f'):\n\t\t\"\"\"\n\t\t获得视频播放地址\n\t\tParameters:\n\t\t\tuser_id：查询的用户UID\n\t\tReturns:\n\t\t\tvideo_names: 视频名字列表\n\t\t\tvideo_urls: 视频链接列表\n\t\t\tnickname: 用户昵称\n\t\t\"\"\"\n\t\tvideo_names = []\n\t\tvideo_urls = []\n\t\tshare_urls = []\n\t\tmax_cursor = 0\n\t\thas_more = 1\n\t\tsign_api = 'http://49.233.200.77:5001'\n\t\tshare_user_url = 'https://www.iesdouyin.com/share/user/%s' % user_id\n\t\tshare_user = requests.get(share_user_url, headers=self.headers)\n\t\twhile share_user.status_code != 200:\n\t\t\tshare_user = requests.get(share_user_url, headers=self.headers)\n\t\t_tac_re = re.compile(r\"tac='([\\s\\S]*?)'</script>\")\n\t\ttac = _tac_re.search(share_user.text).group(1)\n\t\t_dytk_re = re.compile(r\"dytk\\s*:\\s*'(.+)'\")\n\t\tdytk = _dytk_re.search(share_user.text).group(1)\n\t\t_nickname_re = re.compile(r'<p class=\"nickname\">(.+?)<\\/p>')\n\t\tnickname = _nickname_re.search(share_user.text).group(1)\n\t\tdata = {\n\t\t\t'tac': tac.split('|')[0],\n\t\t\t'user_id': user_id,\n\t\t}\n\t\treq = requests.post(sign_api, data=data)\n\t\twhile req.status_code != 200:\n\t\t\treq = requests.post(sign_api, data=data)\n\t\tsign = req.json().get('signature')\n\t\tuser_url_prefix = 'https://www.iesdouyin.com/web/api/v2/aweme/like' if type_flag == 'f' else 'https://www.iesdouyin.com/web/api/v2/aweme/post'\n\t\tprint('解析视频链接中')\n\t\twhile has_more != 0:\n\t\t\tuser_url = user_url_prefix + '/?user_id=%s&sec_uid=&count=21&max_cursor=%s&aid=1128&_signature=%s&dytk=%s' % (user_id, max_cursor, sign, dytk)\n\t\t\treq = requests.get(user_url, headers=self.headers)\n\t\t\twhile req.status_code != 200:\n\t\t\t\treq = requests.get(user_url, headers=self.headers)\n\t\t\thtml = json.loads(req.text)\n\t\t\tfor each in html['aweme_list']:\n\t\t\t\ttry:\n\t\t\t\t\turl = 'https://aweme.snssdk.com/aweme/v1/play/?video_id=%s&line=0&ratio=720p&media_type=4&vr_type=0&improve_bitrate=0&is_play_url=1&is_support_h265=0&source=PackSourceEnum_PUBLISH'\n\t\t\t\t\tvid = each['video']['vid']\n\t\t\t\t\tvideo_url = url % vid\n\t\t\t\texcept:\n\t\t\t\t\tcontinue\n\t\t\t\tshare_desc = each['desc']\n\t\t\t\tif os.name == 'nt':\n\t\t\t\t\tfor c in r'\\/:*?\"<>|':\n\t\t\t\t\t\tnickname = nickname.replace(c, '').strip().strip('\\.')\n\t\t\t\t\t\tshare_desc = share_desc.replace(c, '').strip()\n\t\t\t\tshare_id = each['aweme_id']\n\t\t\t\tif share_desc in ['抖音-原创音乐短视频社区', 'TikTok', '']:\n\t\t\t\t\tvideo_names.append(share_id + '.mp4')\n\t\t\t\telse:\n\t\t\t\t\tvideo_names.append(share_id + '-' + share_desc + '.mp4')\n\t\t\t\tshare_url = 'https://www.iesdouyin.com/share/video/%s' % share_id\n\t\t\t\tshare_urls.append(share_url)\n\t\t\t\tvideo_urls.append(video_url)\n\t\t\tmax_cursor = html['max_cursor']\n\t\t\thas_more = html['has_more']\n\n\t\treturn video_names, video_urls, share_urls, nickname\n\n\tdef get_download_url(self, video_url, watermark_flag):\n\t\t\"\"\"\n\t\t获得带水印的视频播放地址\n\t\tParameters:\n\t\t\tvideo_url：带水印的视频播放地址\n\t\tReturns:\n\t\t\tdownload_url: 带水印的视频下载地址\n\t\t\"\"\"\n\t\t# 带水印视频\n\t\tif watermark_flag == True:\n\t\t\tdownload_url = video_url.replace('/play/', '/playwm/')\n\t\t# 无水印视频\n\t\telse:\n\t\t\tdownload_url = video_url.replace('/playwm/', '/play/')\n\n\t\treturn download_url\n\n\tdef video_downloader(self, video_url, video_name, watermark_flag=False):\n\t\t\"\"\"\n\t\t视频下载\n\t\tParameters:\n\t\t\tvideo_url: 带水印的视频地址\n\t\t\tvideo_name: 视频名\n\t\t\twatermark_flag: 是否下载带水印的视频\n\t\tReturns:\n\t\t\t无\n\t\t\"\"\"\n\t\tsize = 0\n\t\tvideo_url = self.get_download_url(video_url, watermark_flag=watermark_flag)\n\t\twith closing(requests.get(video_url, headers=self.headers1, stream=True)) as response:\n\t\t\tchunk_size = 1024\n\t\t\tcontent_size = int(response.headers['content-length'])\n\t\t\tif response.status_code == 200:\n\t\t\t\tsys.stdout.write('  [文件大小]:%0.2f MB\\n' % (content_size / chunk_size / 1024))\n\n\t\t\t\twith open(video_name, 'wb') as file:\n\t\t\t\t\tfor data in response.iter_content(chunk_size = chunk_size):\n\t\t\t\t\t\tfile.write(data)\n\t\t\t\t\t\tsize += len(data)\n\t\t\t\t\t\tfile.flush()\n\n\t\t\t\t\t\tsys.stdout.write('  [下载进度]:%.2f%%' % float(size / content_size * 100) + '\\r')\n\t\t\t\t\t\tsys.stdout.flush()\n\n\tdef run(self):\n\t\t\"\"\"\n\t\t运行函数\n\t\tParameters:\n\t\t\tNone\n\t\tReturns:\n\t\t\tNone\n\t\t\"\"\"\n\t\tself.hello()\n\t\tprint('UID取得方式：\\n分享用户页面，用浏览器打开短链接，原始链接中/share/user/后的数字即是UID')\n\t\tuser_id = input('请输入UID (例如60388937600):')\n\t\tuser_id = user_id if user_id else '60388937600'\n\t\twatermark_flag = input('是否下载带水印的视频 (0-否(默认), 1-是):')\n\t\twatermark_flag = watermark_flag if watermark_flag!='' else '0'\n\t\twatermark_flag = bool(int(watermark_flag))\n\t\ttype_flag = input('f-收藏的(默认), p-上传的:')\n\t\ttype_flag = type_flag if type_flag!='' else 'f'\n\t\tsave_dir = input('保存路径 (例如\"E:/Download/\", 默认\"./Download/\"):')\n\t\tsave_dir = save_dir if save_dir else \"./Download/\"\n\t\tvideo_names, video_urls, share_urls, nickname = self.get_video_urls(user_id, type_flag)\n\t\tnickname_dir = os.path.join(save_dir, nickname)\n\t\tif not os.path.exists(save_dir):\n\t\t\tos.makedirs(save_dir)\n\t\tif nickname not in os.listdir(save_dir):\n\t\t\tos.mkdir(nickname_dir)\n\t\tif type_flag == 'f':\n\t\t\tif 'favorite' not in os.listdir(nickname_dir):\n\t\t\t\tos.mkdir(os.path.join(nickname_dir, 'favorite'))\n\t\tprint('视频下载中:共有%d个作品!\\n' % len(video_urls))\n\t\tfor num in range(len(video_urls)):\n\t\t\tprint('  解析第%d个视频链接 [%s] 中，请稍后!\\n' % (num + 1, share_urls[num]))\n\t\t\tif '\\\\' in video_names[num]:\n\t\t\t\tvideo_name = video_names[num].replace('\\\\', '')\n\t\t\telif '/' in video_names[num]:\n\t\t\t\tvideo_name = video_names[num].replace('/', '')\n\t\t\telse:\n\t\t\t\tvideo_name = video_names[num]\n\t\t\tvideo_path = os.path.join(nickname_dir, video_name) if type_flag!='f' else os.path.join(nickname_dir, 'favorite', video_name)\n\t\t\tif os.path.isfile(video_path):\n\t\t\t\tprint('视频已存在')\n\t\t\telse:\n\t\t\t\tself.video_downloader(video_urls[num], video_path, watermark_flag)\n\t\t\tprint('\\n')\n\t\tprint('下载完成!')\n\n\tdef hello(self):\n\t\t\"\"\"\n\t\t打印欢迎界面\n\t\tParameters:\n\t\t\tNone\n\t\tReturns:\n\t\t\tNone\n\t\t\"\"\"\n\t\tprint('*' * 100)\n\t\tprint('\\t\\t\\t\\t抖音App视频下载小助手')\n\t\tprint('\\t\\t作者:Jack Cui、steven7851')\n\t\tprint('*' * 100)\n\n\nif __name__ == '__main__':\n\tdouyin = DouYin()\n\tdouyin.run()\n"
  },
  {
    "path": "douyin/fuck-byted-acrawler.js",
    "content": "// Referer:https://raw.githubusercontent.com/loadchange/amemv-crawler/master/fuck-byted-acrawler.js\nfunction generateSignature(userId) {\n    this.navigator = {\n        userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1\"\n    }\n    var e = {}\n\n    var r = (function () {\n        function e(e, a, r) {\n            return (b[e] || (b[e] = t(\"x,y\", \"return x \" + e + \" y\")))(r, a)\n        }\n\n        function a(e, a, r) {\n            return (k[r] || (k[r] = t(\"x,y\", \"return new x[y](\" + Array(r + 1).join(\",x[++y]\").substr(1) + \")\")))(e, a)\n        }\n\n        function r(e, a, r) {\n            var n, t, s = {}, b = s.d = r ? r.d + 1 : 0;\n            for (s[\"$\" + b] = s, t = 0; t < b; t++) s[n = \"$\" + t] = r[n];\n            for (t = 0, b = s.length = a.length; t < b; t++) s[t] = a[t];\n            return c(e, 0, s)\n        }\n\n        function c(t, b, k) {\n            function u(e) {\n                v[x++] = e\n            }\n\n            function f() {\n                return g = t.charCodeAt(b++) - 32, t.substring(b, b += g)\n            }\n\n            function l() {\n                try {\n                    y = c(t, b, k)\n                } catch (e) {\n                    h = e, y = l\n                }\n            }\n\n            for (var h, y, d, g, v = [], x = 0; ;) switch (g = t.charCodeAt(b++) - 32) {\n                case 1:\n                    u(!v[--x]);\n                    break;\n                case 4:\n                    v[x++] = f();\n                    break;\n                case 5:\n                    u(function (e) {\n                        var a = 0, r = e.length;\n                        return function () {\n                            var c = a < r;\n                            return c && u(e[a++]), c\n                        }\n                    }(v[--x]));\n                    break;\n                case 6:\n                    y = v[--x], u(v[--x](y));\n                    break;\n                case 8:\n                    if (g = t.charCodeAt(b++) - 32, l(), b += g, g = t.charCodeAt(b++) - 32, y === c) b += g; else if (y !== l) return y;\n                    break;\n                case 9:\n                    v[x++] = c;\n                    break;\n                case 10:\n                    u(s(v[--x]));\n                    break;\n                case 11:\n                    y = v[--x], u(v[--x] + y);\n                    break;\n                case 12:\n                    for (y = f(), d = [], g = 0; g < y.length; g++) d[g] = y.charCodeAt(g) ^ g + y.length;\n                    u(String.fromCharCode.apply(null, d));\n                    break;\n                case 13:\n                    y = v[--x], h = delete v[--x][y];\n                    break;\n                case 14:\n                    v[x++] = t.charCodeAt(b++) - 32;\n                    break;\n                case 59:\n                    u((g = t.charCodeAt(b++) - 32) ? (y = x, v.slice(x -= g, y)) : []);\n                    break;\n                case 61:\n                    u(v[--x][t.charCodeAt(b++) - 32]);\n                    break;\n                case 62:\n                    g = v[--x], k[0] = 65599 * k[0] + k[1].charCodeAt(g) >>> 0;\n                    break;\n                case 65:\n                    h = v[--x], y = v[--x], v[--x][y] = h;\n                    break;\n                case 66:\n                    u(e(t[b++], v[--x], v[--x]));\n                    break;\n                case 67:\n                    y = v[--x], d = v[--x], u((g = v[--x]).x === c ? r(g.y, y, k) : g.apply(d, y));\n                    break;\n                case 68:\n                    u(e((g = t[b++]) < \"<\" ? (b--, f()) : g + g, v[--x], v[--x]));\n                    break;\n                case 70:\n                    u(!1);\n                    break;\n                case 71:\n                    v[x++] = n;\n                    break;\n                case 72:\n                    v[x++] = +f();\n                    break;\n                case 73:\n                    u(parseInt(f(), 36));\n                    break;\n                case 75:\n                    if (v[--x]) {\n                        b++;\n                        break\n                    }\n                case 74:\n                    g = t.charCodeAt(b++) - 32 << 16 >> 16, b += g;\n                    break;\n                case 76:\n                    u(k[t.charCodeAt(b++) - 32]);\n                    break;\n                case 77:\n                    y = v[--x], u(v[--x][y]);\n                    break;\n                case 78:\n                    g = t.charCodeAt(b++) - 32, u(a(v, x -= g + 1, g));\n                    break;\n                case 79:\n                    g = t.charCodeAt(b++) - 32, u(k[\"$\" + g]);\n                    break;\n                case 81:\n                    h = v[--x], v[--x][f()] = h;\n                    break;\n                case 82:\n                    u(v[--x][f()]);\n                    break;\n                case 83:\n                    h = v[--x], k[t.charCodeAt(b++) - 32] = h;\n                    break;\n                case 84:\n                    v[x++] = !0;\n                    break;\n                case 85:\n                    v[x++] = void 0;\n                    break;\n                case 86:\n                    u(v[x - 1]);\n                    break;\n                case 88:\n                    h = v[--x], y = v[--x], v[x++] = h, v[x++] = y;\n                    break;\n                case 89:\n                    u(function () {\n                        function e() {\n                            return r(e.y, arguments, k)\n                        }\n\n                        return e.y = f(), e.x = c, e\n                    }());\n                    break;\n                case 90:\n                    v[x++] = null;\n                    break;\n                case 91:\n                    v[x++] = h;\n                    break;\n                case 93:\n                    h = v[--x];\n                    break;\n                case 0:\n                    return v[--x];\n                default:\n                    u((g << 16 >> 16) - 16)\n            }\n        }\n\n        var n = this, t = n.Function, s = Object.keys || function (e) {\n            var a = {}, r = 0;\n            for (var c in e) a[r++] = c;\n            return a.length = r, a\n        }, b = {}, k = {};\n        return r\n    })()\n    ('gr$Daten Иb/s!l y͒yĹg,(lfi~ah`{mv,-n|jqewVxp{rvmmx,&effkx[!cs\"l\".Pq%widthl\"@q&heightl\"vr*getContextx$\"2d[!cs#l#,*;?|u.|uc{uq$fontl#vr(fillTextx$$龘ฑภ경2<[#c}l#2q*shadowBlurl#1q-shadowOffsetXl#$$limeq+shadowColorl#vr#arcx88802[%c}l#vr&strokex[ c}l\"v,)}eOmyoZB]mx[ cs!0s$l$Pb<k7l l!r&lengthb%^l$1+s$j\u0002l  s#i$1ek1s$gr#tack4)zgr#tac$! +0o![#cj?o ]!l$b%s\"o ]!l\"l$b*b^0d#>>>s!0s%yA0s\"l\"l!r&lengthb<k+l\"^l\"1+s\"j\u0005l  s&l&z0l!$ +[\"cs\\'(0l#i\\'1ps9wxb&s() &{s)/s(gr&Stringr,fromCharCodes)0s*yWl ._b&s o!])l l Jb<k$.aj;l .Tb<k$.gj/l .^b<k&i\"-4j!\u001f+& s+yPo!]+s!l!l Hd>&l!l Bd>&+l!l <d>&+l!l 6d>&+l!l &+ s,y=o!o!]/q\"13o!l q\"10o!],l 2d>& s.{s-yMo!o!]0q\"13o!]*Ld<l 4d#>>>b|s!o!l q\"10o!],l!& s/yIo!o!].q\"13o!],o!]*Jd<l 6d#>>>b|&o!]+l &+ s0l-l!&l-l!i\\'1z141z4b/@d<l\"b|&+l-l(l!b^&+l-l&zl\\'g,)gk}ejo{cm,)|yn~Lij~em[\"cl$b%@d<l&zl\\'l $ +[\"cl$b%b|&+l-l%8d<@b|l!b^&+ q$sign ', [e])\n    return e.sign(userId)\n}\n\nvar _ = process.argv.splice(2)\n\nconsole.log(generateSignature(_[0]))\n"
  },
  {
    "path": "douyin.py",
    "content": "# -*- coding:utf-8 -*-\nfrom bs4 import BeautifulSoup\nfrom contextlib import closing\nimport requests, json, time, re, os, sys, time\n\nclass DouYin(object):\n\tdef __init__(self):\n\t\t\"\"\"\n\t\t抖音App视频下载\n\t\t\"\"\"\n\t\t#SSL认证\n\t\tpass\n\n\tdef get_video_urls(self, user_id):\n\t\t\"\"\"\n\t\t获得视频播放地址\n\t\tParameters:\n\t\t\tnickname：查询的用户名\n\t\tReturns:\n\t\t\tvideo_names: 视频名字列表\n\t\t\tvideo_urls: 视频链接列表\n\t\t\taweme_count: 视频数量\n\t\t\"\"\"\n\t\tvideo_names = []\n\t\tvideo_urls = []\n\t\tunique_id = ''\n\t\twhile unique_id != user_id:\n\t\t\tsearch_url = 'https://api.amemv.com/aweme/v1/discover/search/?cursor=0&keyword=%s&count=10&type=1&retry_type=no_retry&iid=17900846586&device_id=34692364855&ac=wifi&channel=xiaomi&aid=1128&app_name=aweme&version_code=162&version_name=1.6.2&device_platform=android&ssmix=a&device_type=MI+5&device_brand=Xiaomi&os_api=24&os_version=7.0&uuid=861945034132187&openudid=dc451556fc0eeadb&manifest_version_code=162&resolution=1080*1920&dpi=480&update_version_code=1622' % user_id\n\t\t\treq = requests.get(url = search_url, verify = False)\n\t\t\thtml = json.loads(req.text)\n\t\t\taweme_count = html['user_list'][0]['user_info']['aweme_count']\n\t\t\tuid = html['user_list'][0]['user_info']['uid']\n\t\t\tnickname = html['user_list'][0]['user_info']['nickname']\n\t\t\tunique_id = html['user_list'][0]['user_info']['unique_id']\n\t\tuser_url = 'https://www.douyin.com/aweme/v1/aweme/post/?user_id=%s&max_cursor=0&count=%s' % (uid, aweme_count)\n\t\treq = requests.get(url = user_url, verify = False)\n\t\thtml = json.loads(req.text)\n\t\ti = 1\n\t\tfor each in html['aweme_list']:\n\t\t\tshare_desc = each['share_info']['share_desc']\n\t\t\tif '抖音-原创音乐短视频社区' == share_desc:\n\t\t\t\tvideo_names.append(str(i) + '.mp4')\n\t\t\t\ti += 1\n\t\t\telse:\n\t\t\t\tvideo_names.append(share_desc + '.mp4')\n\t\t\tvideo_urls.append(each['share_info']['share_url'])\n\n\t\treturn video_names, video_urls, nickname\n\n\tdef get_download_url(self, video_url):\n\t\t\"\"\"\n\t\t获得视频播放地址\n\t\tParameters:\n\t\t\tvideo_url：视频播放地址\n\t\tReturns:\n\t\t\tdownload_url: 视频下载地址\n\t\t\"\"\"\n\t\treq = requests.get(url = video_url, verify = False)\n\t\tbf = BeautifulSoup(req.text, 'lxml')\n\t\tscript = bf.find_all('script')[-1]\n\t\tvideo_url_js = re.findall('var data = \\[(.+)\\];', str(script))[0]\n\t\tvideo_html = json.loads(video_url_js)\n\t\tdownload_url = video_html['video']['play_addr']['url_list'][0]\n\t\treturn download_url\n\n\tdef video_downloader(self, video_url, video_name):\n\t\t\"\"\"\n\t\t视频下载\n\t\tParameters:\n\t\t\tNone\n\t\tReturns:\n\t\t\tNone\n\t\t\"\"\"\n\t\tsize = 0\n\t\twith closing(requests.get(video_url, stream=True, verify = False)) as response:\n\t\t\tchunk_size = 1024\n\t\t\tcontent_size = int(response.headers['content-length']) \n\t\t\tif response.status_code == 200:\n\t\t\t\tsys.stdout.write('  [文件大小]:%0.2f MB\\n' % (content_size / chunk_size / 1024))\n\n\t\t\t\twith open(video_name, \"wb\") as file:  \n\t\t\t\t\tfor data in response.iter_content(chunk_size = chunk_size):\n\t\t\t\t\t\tfile.write(data)\n\t\t\t\t\t\tsize += len(data)\n\t\t\t\t\t\tfile.flush()\n\n\t\t\t\t\tsys.stdout.write('    [下载进度]:%.2f%%' % float(size / content_size * 100))\n\t\t\t\t\tsys.stdout.flush()\n\t\ttime.sleep(1)\n\n\n\tdef run(self):\n\t\t\"\"\"\n\t\t运行函数\n\t\tParameters:\n\t\t\tNone\n\t\tReturns:\n\t\t\tNone\n\t\t\"\"\"\n\t\tself.hello()\n\t\t# user_id = input('请输入ID(例如13978338):')\n\t\tuser_id = 'sm666888'\n\t\tvideo_names, video_urls, nickname = self.get_video_urls(user_id)\n\t\tif nickname not in os.listdir():\n\t\t\tos.mkdir(nickname)\n\t\tsys.stdout.write('视频下载中:\\n')\n\t\tfor num in range(len(video_urls)):\n\t\t\tprint('  %s\\n' % video_urls[num])\n\t\t\tvideo_url = self.get_download_url(video_urls[num])\n\t\t\tif '\\\\' in video_names[num]:\n\t\t\t\tvideo_name = video_names[num].replace('\\\\', '')\n\t\t\telif '/' in video_names[num]:\n\t\t\t\tvideo_name = video_names[num].replace('/', '')\n\t\t\telse:\n\t\t\t\tvideo_name = video_names[num]\n\t\t\tself.video_downloader(video_url, os.path.join(nickname, video_name))\n\t\t\tprint('')\n\n\tdef hello(self):\n\t\t\"\"\"\n\t\t打印欢迎界面\n\t\tParameters:\n\t\t\tNone\n\t\tReturns:\n\t\t\tNone\n\t\t\"\"\"\n\t\tprint('*' * 100)\n\t\tprint('\\t\\t\\t\\t抖音App视频下载小助手')\n\t\tprint('*' * 100)\n\n\t\t\nif __name__ == '__main__':\n\tdouyin = DouYin()\n\tdouyin.run()"
  },
  {
    "path": "douyin_pro.py",
    "content": "# -*- coding:utf-8 -*-\nfrom splinter.driver.webdriver.chrome import Options, Chrome\nfrom splinter.browser import Browser\nfrom contextlib import closing\nimport requests, json, time, re, os, sys, time\nfrom bs4 import BeautifulSoup\n\nclass DouYin(object):\n\tdef __init__(self, width = 500, height = 300):\n\t\t\"\"\"\n\t\t抖音App视频下载\n\t\t\"\"\"\n\t\t# 无头浏览器\n\t\tchrome_options = Options()\n\t\tchrome_options.add_argument('user-agent=\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\"')\n\t\tself.driver = Browser(driver_name='chrome', executable_path='D:/chromedriver', options=chrome_options, headless=True)\n\n\tdef get_video_urls(self, user_id):\n\t\t\"\"\"\n\t\t获得视频播放地址\n\t\tParameters:\n\t\t\tuser_id：查询的用户ID\n\t\tReturns:\n\t\t\tvideo_names: 视频名字列表\n\t\t\tvideo_urls: 视频链接列表\n\t\t\tnickname: 用户昵称\n\t\t\"\"\"\n\t\tvideo_names = []\n\t\tvideo_urls = []\n\t\tunique_id = ''\n\t\twhile unique_id != user_id:\n\t\t\tsearch_url = 'https://api.amemv.com/aweme/v1/discover/search/?cursor=0&keyword=%s&count=10&type=1&retry_type=no_retry&iid=17900846586&device_id=34692364855&ac=wifi&channel=xiaomi&aid=1128&app_name=aweme&version_code=162&version_name=1.6.2&device_platform=android&ssmix=a&device_type=MI+5&device_brand=Xiaomi&os_api=24&os_version=7.0&uuid=861945034132187&openudid=dc451556fc0eeadb&manifest_version_code=162&resolution=1080*1920&dpi=480&update_version_code=1622' % user_id\n\t\t\treq = requests.get(url = search_url, verify = False)\n\t\t\thtml = json.loads(req.text)\n\t\t\taweme_count = html['user_list'][0]['user_info']['aweme_count']\n\t\t\tuid = html['user_list'][0]['user_info']['uid']\n\t\t\tnickname = html['user_list'][0]['user_info']['nickname']\n\t\t\tunique_id = html['user_list'][0]['user_info']['unique_id']\n\t\tuser_url = 'https://www.douyin.com/aweme/v1/aweme/post/?user_id=%s&max_cursor=0&count=%s' % (uid, aweme_count)\n\t\treq = requests.get(url = user_url, verify = False)\n\t\thtml = json.loads(req.text)\n\t\ti = 1\n\t\tfor each in html['aweme_list']:\n\t\t\tshare_desc = each['share_info']['share_desc']\n\t\t\tif '抖音-原创音乐短视频社区' == share_desc:\n\t\t\t\tvideo_names.append(str(i) + '.mp4')\n\t\t\t\ti += 1\n\t\t\telse:\n\t\t\t\tvideo_names.append(share_desc + '.mp4')\n\t\t\tvideo_urls.append(each['share_info']['share_url'])\n\n\t\treturn video_names, video_urls, nickname\n\n\tdef get_download_url(self, video_url):\n\t\t\"\"\"\n\t\t获得带水印的视频播放地址\n\t\tParameters:\n\t\t\tvideo_url：带水印的视频播放地址\n\t\tReturns:\n\t\t\tdownload_url: 带水印的视频下载地址\n\t\t\"\"\"\n\t\treq = requests.get(url = video_url, verify = False)\n\t\tbf = BeautifulSoup(req.text, 'lxml')\n\t\tscript = bf.find_all('script')[-1]\n\t\tvideo_url_js = re.findall('var data = \\[(.+)\\];', str(script))[0]\n\t\tvideo_html = json.loads(video_url_js)\n\t\tdownload_url = video_html['video']['play_addr']['url_list'][0]\n\t\treturn download_url\n\n\tdef video_downloader(self, video_url, video_name, watermark_flag=True):\n\t\t\"\"\"\n\t\t视频下载\n\t\tParameters:\n\t\t\tvideo_url: 带水印的视频地址\n\t\t\tvideo_name: 视频名\n\t\t\twatermark_flag: 是否下载不带水印的视频\n\t\tReturns:\n\t\t\t无\n\t\t\"\"\"\n\t\tsize = 0\n\t\tif watermark_flag == True:\n\t\t\tvideo_url = self.remove_watermark(video_url)\n\t\telse:\n\t\t\tvideo_url = self.get_download_url(video_url)\n\t\twith closing(requests.get(video_url, stream=True, verify = False)) as response:\n\t\t\tchunk_size = 1024\n\t\t\tcontent_size = int(response.headers['content-length']) \n\t\t\tif response.status_code == 200:\n\t\t\t\tsys.stdout.write('  [文件大小]:%0.2f MB\\n' % (content_size / chunk_size / 1024))\n\n\t\t\t\twith open(video_name, \"wb\") as file:  \n\t\t\t\t\tfor data in response.iter_content(chunk_size = chunk_size):\n\t\t\t\t\t\tfile.write(data)\n\t\t\t\t\t\tsize += len(data)\n\t\t\t\t\t\tfile.flush()\n\n\t\t\t\t\t\tsys.stdout.write('  [下载进度]:%.2f%%' % float(size / content_size * 100) + '\\r')\n\t\t\t\t\t\tsys.stdout.flush()\n\n\n\tdef remove_watermark(self, video_url):\n\t\t\"\"\"\n\t\t获得无水印的视频播放地址\n\t\tParameters:\n\t\t\tvideo_url: 带水印的视频地址\n\t\tReturns:\n\t\t\t无水印的视频下载地址\n\t\t\"\"\"\n\t\tself.driver.visit('http://douyin.iiilab.com/')\n\t\tself.driver.find_by_tag('input').fill(video_url)\n\t\tself.driver.find_by_xpath('//button[@class=\"btn btn-default\"]').click()\n\t\thtml = self.driver.find_by_xpath('//div[@class=\"thumbnail\"]/div/p')[0].html\n\t\tbf = BeautifulSoup(html, 'lxml')\n\t\treturn bf.find('a').get('href')\n\n\tdef run(self):\n\t\t\"\"\"\n\t\t运行函数\n\t\tParameters:\n\t\t\tNone\n\t\tReturns:\n\t\t\tNone\n\t\t\"\"\"\n\t\tself.hello()\n\t\tuser_id = input('请输入ID(例如40103580):')\n\t\tvideo_names, video_urls, nickname = self.get_video_urls(user_id)\n\t\tif nickname not in os.listdir():\n\t\t\tos.mkdir(nickname)\n\t\tprint('视频下载中:共有%d个作品!\\n' % len(video_urls))\n\t\tfor num in range(len(video_urls)):\n\t\t\tprint('  解析第%d个视频链接 [%s] 中，请稍后!\\n' % (num+1, video_urls[num]))\n\t\t\tif '\\\\' in video_names[num]:\n\t\t\t\tvideo_name = video_names[num].replace('\\\\', '')\n\t\t\telif '/' in video_names[num]:\n\t\t\t\tvideo_name = video_names[num].replace('/', '')\n\t\t\telse:\n\t\t\t\tvideo_name = video_names[num]\n\t\t\tself.video_downloader(video_urls[num], os.path.join(nickname, video_name))\n\t\t\tprint('\\n')\n\n\t\tprint('下载完成!')\n\n\tdef hello(self):\n\t\t\"\"\"\n\t\t打印欢迎界面\n\t\tParameters:\n\t\t\tNone\n\t\tReturns:\n\t\t\tNone\n\t\t\"\"\"\n\t\tprint('*' * 100)\n\t\tprint('\\t\\t\\t\\t抖音App视频下载小助手')\n\t\tprint('\\t\\t作者:Jack Cui')\n\t\tprint('*' * 100)\n\n\nif __name__ == '__main__':\n\tdouyin = DouYin()\n\tdouyin.run()\n"
  },
  {
    "path": "downloader.py",
    "content": "#-*- coding: UTF-8 -*-\nimport requests  \nfrom contextlib import closing\n\nclass ProgressBar(object):  \n    def __init__(self, title, count=0.0, run_status=None, fin_status=None, total=100.0, unit='', sep='/', chunk_size=1.0):  \n        super(ProgressBar, self).__init__()  \n        self.info = \"[%s] %s %.2f %s %s %.2f %s\"  \n        self.title = title  \n        self.total = total  \n        self.count = count  \n        self.chunk_size = chunk_size  \n        self.status = run_status or \"\"  \n        self.fin_status = fin_status or \" \" * len(self.status)  \n        self.unit = unit  \n        self.seq = sep  \n  \n    def __get_info(self):  \n        #[名称] 状态 进度 单位 分割线 总数 单位  \n        _info = self.info % (self.title, self.status, self.count/self.chunk_size, self.unit, self.seq, self.total/self.chunk_size, self.unit)  \n        return _info  \n  \n    def refresh(self, count = 1, status = None):  \n        self.count += count  \n        self.status = status or self.status  \n        end_str = \"\\r\"  \n        if self.count >= self.total:  \n            end_str = '\\n'  \n            self.status = status or self.fin_status  \n        print(self.__get_info(), end=end_str, )  \n\n\nif __name__ == '__main__':\n\t#url = 'http://www.demongan.com/source/game/二十四点.zip'\n\t#filename = '二十四点.zip'\n\tprint('*' * 100)\n\tprint('\\t\\t\\t\\t欢迎使用文件下载小助手')\n\tprint('作者:Jack-Cui\\n博客:http://blog.csdn.net/c406495762')\n\tprint('*' * 100)\n\turl  = input('请输入需要下载的文件链接:\\n')\n\tfilename = url.split('/')[-1]\n\twith closing(requests.get(url, stream=True)) as response:  \n\t\tchunk_size = 1024  \n\t\tcontent_size = int(response.headers['content-length'])  \n\t\tif response.status_code == 200:\n\t\t\tprint('文件大小:%0.2f KB' % (content_size / chunk_size))\n\t\t\tprogress = ProgressBar(\"%s下载进度\" % filename\n\t\t\t            , total = content_size  \n\t\t\t            , unit = \"KB\"  \n\t\t\t            , chunk_size = chunk_size  \n\t\t\t            , run_status = \"正在下载\"  \n\t\t\t            , fin_status = \"下载完成\")  \n\n\t\t\twith open(filename, \"wb\") as file:  \n\t\t\t        for data in response.iter_content(chunk_size=chunk_size):  \n\t\t\t            file.write(data)  \n\t\t\t            progress.refresh(count=len(data))  \n\t\telse:\n\t\t\tprint('链接异常')"
  },
  {
    "path": "financical.py",
    "content": "#-*- coding:UTF-8 -*-\nimport sys\nimport pymysql\nimport requests\nimport json\nimport re\nfrom bs4 import BeautifulSoup\n\n\"\"\"\n类说明:获取财务数据\n\nAuthor:\n\tJack Cui\nBlog:\n\thttp://blog.csdn.net/c406495762\nZhihu:\n\thttps://www.zhihu.com/people/Jack--Cui/\nModify:\n\t2017-08-31\n\"\"\"\nclass FinancialData():\n\n\tdef __init__(self):\n\t\t#服务器域名\n\t\tself.server = 'http://quotes.money.163.com/'\n\t\tself.cwnb = 'http://quotes.money.163.com/hkstock/cwsj_'\n\t\t#主要财务指标\n\t\tself.cwzb_dict = {'EPS':'基本每股收益','EPS_DILUTED':'摊薄每股收益','GROSS_MARGIN':'毛利率',\n\t\t'CAPITAL_ADEQUACY':'资本充足率','LOANS_DEPOSITS':'贷款回报率','ROTA':'总资产收益率',\n\t\t'ROEQUITY':'净资产收益率','CURRENT_RATIO':'流动比率','QUICK_RATIO':'速动比率',\n\t\t'ROLOANS':'存贷比','INVENTORY_TURNOVER':'存货周转率','GENERAL_ADMIN_RATIO':'管理费用比率',\n\t\t'TOTAL_ASSET2TURNOVER':'资产周转率','FINCOSTS_GROSSPROFIT':'财务费用比率','TURNOVER_CASH':'销售现金比率','YEAREND_DATE':'报表日期'}\n\t\t#利润表\n\t\tself.lrb_dict = {'TURNOVER':'总营收','OPER_PROFIT':'经营利润','PBT':'除税前利润',\n\t\t'NET_PROF':'净利润','EPS':'每股基本盈利','DPS':'每股派息',\n\t\t'INCOME_INTEREST':'利息收益','INCOME_NETTRADING':'交易收益','INCOME_NETFEE':'费用收益','YEAREND_DATE':'报表日期'}\n\t\t#资产负债表\n\t\tself.fzb_dict = {\n\t\t\t'FIX_ASS':'固定资产','CURR_ASS':'流动资产','CURR_LIAB':'流动负债',\n\t\t\t'INVENTORY':'存款','CASH':'现金及银行存结','OTHER_ASS':'其他资产',\n\t\t\t'TOTAL_ASS':'总资产','TOTAL_LIAB':'总负债','EQUITY':'股东权益',\n\t\t\t'CASH_SHORTTERMFUND':'库存现金及短期资金','DEPOSITS_FROM_CUSTOMER':'客户存款',\n\t\t\t'FINANCIALASSET_SALE':'可供出售之证券','LOAN_TO_BANK':'银行同业存款及贷款',\n\t\t\t'DERIVATIVES_LIABILITIES':'金融负债','DERIVATIVES_ASSET':'金融资产','YEAREND_DATE':'报表日期'}\n\t\t#现金流表\n\t\tself.llb_dict = {\n\t\t\t'CF_NCF_OPERACT':'经营活动产生的现金流','CF_INT_REC':'已收利息','CF_INT_PAID':'已付利息',\n\t\t\t'CF_INT_REC':'已收股息','CF_DIV_PAID':'已派股息','CF_INV':'投资活动产生现金流',\n\t\t\t'CF_FIN_ACT':'融资活动产生现金流','CF_BEG':'期初现金及现金等价物','CF_CHANGE_CSH':'现金及现金等价物净增加额',\n\t\t\t'CF_END':'期末现金及现金等价物','CF_EXCH':'汇率变动影响','YEAREND_DATE':'报表日期'}\n\t\t#总表\n\t\tself.table_dict = {'cwzb':self.cwzb_dict,'lrb':self.lrb_dict,'fzb':self.fzb_dict,'llb':self.llb_dict}\n\t\t#请求头\n\t\tself.headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',\n\t\t\t'Accept-Encoding': 'gzip, deflate',\n\t\t\t'Accept-Language': 'zh-CN,zh;q=0.8',\n\t\t\t'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36'}\n\t\n\t\"\"\"\n\t函数说明:获取股票页面信息\n\n\tAuthor:\n\t\tJack Cui\n\tParameters:\n\t    url - 股票财务数据界面地址\n\tReturns:\n\t    name - 股票名\n\t    table_name_list - 财务报表名称\n\t    table_date_list - 财务报表年限\n\t    url_list - 财务报表查询连接\n\tBlog:\n\t\thttp://blog.csdn.net/c406495762\n\tZhihu:\n\t\thttps://www.zhihu.com/people/Jack--Cui/\n\tModify:\n\t\t2017-08-31\n\t\"\"\"\n\tdef get_informations(self, url):\n\t\treq = requests.get(url = url, headers = self.headers)\n\t\treq.encoding = 'utf-8'\n\t\thtml = req.text\n\t\tpage_bf = BeautifulSoup(html, 'lxml')\n\t\t#股票名称，股票代码\n\t\tname = page_bf.find_all('span', class_ = 'name')[0].string\n\t\t# code = page_bf.find_all('span', class_ = 'code')[0].string\n\t\t# code = re.findall('\\d+',code)[0]\n\n\t\t#存储各个表名的列表\n\t\ttable_name_list = []\n\t\ttable_date_list = []\n\t\teach_date_list = []\n\t\turl_list = []\n\t\t#表名和表时间\n\t\ttable_name = page_bf.find_all('div', class_ = 'titlebar3')\n\t\tfor each_table_name in table_name:\n\t\t\t#表名\n\t\t\ttable_name_list.append(each_table_name.span.string)\n\t\t\t#表时间\n\t\t\tfor each_table_date in each_table_name.div.find_all('select', id = re.compile('.+1$')):\n\t\t\t\turl_list.append(re.findall('(\\w+)1',each_table_date.get('id'))[0])\n\t\t\t\tfor each_date in each_table_date.find_all('option'):\n\t\t\t\t\teach_date_list.append(each_date.string)\n\t\t\t\ttable_date_list.append(each_date_list)\n\t\t\t\teach_date_list = []\n\t\treturn name,table_name_list,table_date_list,url_list\n\n\t\"\"\"\n\t函数说明:财务报表入库\n\n\tAuthor:\n\t\tJack Cui\n\tParameters:\n\t    name - 股票名\n\t    table_name_list - 财务报表名称\n\t    table_date_list - 财务报表年限\n\t    url_list - 财务报表查询连接\n\tReturns:\n\t\t无\n\tBlog:\n\t\thttp://blog.csdn.net/c406495762\n\tZhihu:\n\t\thttps://www.zhihu.com/people/Jack--Cui/\n\tModify:\n\t\t2017-08-31\n\t\"\"\"\n\tdef insert_tables(self, name, table_name_list,table_date_list, url_list):\n\t\t#打开数据库连接:host-连接主机地址,port-端口号,user-用户名,passwd-用户密码,db-数据库名,charset-编码\n\t\tconn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='yourpasswd',db='financialdata',charset='utf8')\n\t\t#使用cursor()方法获取操作游标\n\t\tcursor = conn.cursor()  \n\t\t#插入信息\n\t\tfor i in range(len(table_name_list)):\n\t\t\tsys.stdout.write('    [正在下载       ]    %s' % table_name_list[i] + '\\r')\n\t\t\t#获取数据地址\n\t\t\turl = self.server + 'hk/service/cwsj_service.php?symbol={}&start={}&end={}&type={}&unit=yuan'.format(code,table_date_list[i][-1],table_date_list[i][0],url_list[i])\n\t\t\treq_table = requests.get(url = url, headers = self.headers)\n\t\t\ttable = req_table.json()\n\t\t\tnums = len(table)\n\t\t\tvalue_dict = {}\n\t\t\tfor num in range(nums):\n\t\t\t\tsys.stdout.write('    [正在下载 %.2f%%]   ' % (((num+1) / nums)*100) + '\\r')\n\t\t\t\tsys.stdout.flush()\n\t\t\t\tvalue_dict['股票名'] = name\n\t\t\t\tvalue_dict['股票代码'] = code\n\t\t\t\tfor key, value in table[i].items():\n\t\t\t\t\tif key in self.table_dict[url_list[i]]:\n\t\t\t\t\t\tvalue_dict[self.table_dict[url_list[i]][key]] = value\n\n\t\t\t\tsql1 = \"\"\"\n\t\t\t\tINSERT INTO %s (`股票名`,`股票代码`,`报表日期`) VALUES ('%s','%s','%s')\"\"\" % (url_list[i],value_dict['股票名'],value_dict['股票代码'],value_dict['报表日期'])\n\t\t\t\ttry:\n\t\t\t\t\tcursor.execute(sql1)\n\t\t\t\t\t# 执行sql语句\n\t\t\t\t\tconn.commit()\n\t\t\t\texcept:\n\t\t\t\t\t# 发生错误时回滚\n\t\t\t\t\tconn.rollback()\n\n\t\t\t\tfor key, value in value_dict.items():\n\t\t\t\t\tif key not in ['股票名','股票代码','报表日期']:\n\t\t\t\t\t\tsql2 = \"\"\"\n\t\t\t\t\t\tUPDATE %s SET %s='%s' WHERE `股票名`='%s' AND `报表日期`='%s'\"\"\" % (url_list[i],key,value,value_dict['股票名'],value_dict['报表日期'])\n\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\tcursor.execute(sql2)\n\t\t\t\t\t\t\t# 执行sql语句\n\t\t\t\t\t\t\tconn.commit()\n\t\t\t\t\t\texcept:\n\t\t\t\t\t\t\t# 发生错误时回滚\n\t\t\t\t\t\t\tconn.rollback()\n\t\t\t\tvalue_dict = {}\n\t\t\tprint('    [下载完成 ')\n\n\t\t# 关闭数据库连接\n\t\tcursor.close()  \n\t\tconn.close()\n\nif __name__ == '__main__':\n\tprint('*' * 100)\n\tprint('\\t\\t\\t\\t\\t财务数据下载助手\\n')\n\tprint('作者:Jack-Cui\\n')\n\tprint('About Me:\\n')\n\tprint('  知乎:https://www.zhihu.com/people/Jack--Cui')\n\tprint('  Blog:http://blog.csdn.net/c406495762')\n\tprint('  Gihub:https://github.com/Jack-Cherish\\n')\n\tprint('*' * 100)\n\tfd = FinancialData()\n\t#上市股票地址\n\tcode = input('请输入股票代码:')\n\n\tname,table_name_list,table_date_list,url_list = fd.get_informations(fd.cwnb + code + '.html')\n\tprint('\\n  %s:(%s)财务数据下载中！\\n' % (name,code))\n\tfd.insert_tables(name,table_name_list,table_date_list,url_list)\n\tprint('\\n  %s:(%s)财务数据下载完成！' % (name,code))"
  },
  {
    "path": "geetest.py",
    "content": "# -*-coding:utf-8 -*-\nimport random\nimport re\nimport time\n# 图片转换\nimport base64\nfrom urllib.request import urlretrieve\n\nfrom bs4 import BeautifulSoup\n\nimport PIL.Image as image\nfrom selenium import webdriver\nfrom selenium.webdriver import ActionChains\nfrom selenium.webdriver.common.by import By\nfrom selenium.webdriver.support import expected_conditions as EC\nfrom selenium.webdriver.support.ui import WebDriverWait\n\ndef save_base64img(data_str, save_name):\n    \"\"\"\n    将 base64 数据转化为图片保存到指定位置\n    :param data_str: base64 数据，不包含类型\n    :param save_name: 保存的全路径\n    \"\"\"\n    img_data = base64.b64decode(data_str)\n    file = open(save_name, 'wb')\n    file.write(img_data)\n    file.close()\n\n\ndef get_base64_by_canvas(driver, class_name, contain_type):\n    \"\"\"\n    将 canvas 标签内容转换为 base64 数据\n    :param driver: webdriver 对象\n    :param class_name: canvas 标签的类名\n    :param contain_type: 返回的数据是否包含类型\n    :return: base64 数据\n    \"\"\"\n    # 防止图片未加载完就下载一张空图\n    bg_img = ''\n    while len(bg_img) < 5000:\n        getImgJS = 'return document.getElementsByClassName(\"' + class_name + '\")[0].toDataURL(\"image/png\");'\n        bg_img = driver.execute_script(getImgJS)\n        time.sleep(0.5)\n    # print(bg_img)\n    if contain_type:\n        return bg_img\n    else:\n        return bg_img[bg_img.find(',') + 1:]\n\n\ndef save_bg(driver, bg_path=\"bg.png\", bg_class=\"geetest_canvas_bg geetest_absolute\"):\n    \"\"\"\n    保存包含缺口的背景图\n    :param driver: webdriver 对象\n    :param bg_path: 保存路径\n    :param bg_class: 背景图的 class 属性\n    :return: 保存路径\n    \"\"\"\n    bg_img_data = get_base64_by_canvas(driver, bg_class, False)\n    save_base64img(bg_img_data, bg_path)\n    return bg_path\n\n\ndef save_full_bg(driver, full_bg_path=\"fbg.png\", full_bg_class=\"geetest_canvas_fullbg geetest_fade geetest_absolute\"):\n    \"\"\"\n    保存完整的的背景图\n    :param driver: webdriver 对象\n    :param full_bg_path: 保存路径\n    :param full_bg_class: 完整背景图的 class 属性\n    :return: 保存路径\n    \"\"\"\n    bg_img_data = get_base64_by_canvas(driver, full_bg_class, False)\n    save_base64img(bg_img_data, full_bg_path)\n    return full_bg_path\n\nclass Crack():\n\tdef __init__(self,keyword):\n\t\tself.url = '*'\n\t\tself.browser = webdriver.Chrome('D:\\\\chromedriver.exe')\n\t\tself.wait = WebDriverWait(self.browser, 100)\n\t\tself.keyword = keyword\n\t\tself.BORDER = 6\n\n\tdef open(self):\n\t\t\"\"\"\n\t\t打开浏览器,并输入查询内容\n\t\t\"\"\"\n\t\tself.browser.get(self.url)\n\t\tkeyword = self.wait.until(EC.presence_of_element_located((By.ID, 'keyword_qycx')))\n\t\tbowton = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'btn')))\n\t\tkeyword.send_keys(self.keyword)\n\t\tbowton.click()\n\n\tdef get_images(self, bg_filename = 'bg.jpg', fullbg_filename = 'fullbg.jpg'):\n\t\t\"\"\"\n\t\t获取验证码图片\n\t\t:return: 图片的location信息\n\t\t\"\"\"\n\t\tbg = []\n\t\tfullgb = []\n\t\twhile bg == [] and fullgb == []:\n\t\t\tbf = BeautifulSoup(self.browser.page_source, 'lxml')\n\t\t\tbg = bf.find_all('div', class_ = 'gt_cut_bg_slice')\n\t\t\tfullgb = bf.find_all('div', class_ = 'gt_cut_fullbg_slice')\n\t\tbg_url = re.findall('url\\(\\\"(.*)\\\"\\);', bg[0].get('style'))[0].replace('webp', 'jpg')\n\t\tfullgb_url = re.findall('url\\(\\\"(.*)\\\"\\);', fullgb[0].get('style'))[0].replace('webp', 'jpg')\n\t\tbg_location_list = []\n\t\tfullbg_location_list = []\n\t\tfor each_bg in bg:\n\t\t\tlocation = {}\n\t\t\tlocation['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][0])\n\t\t\tlocation['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][1])\n\t\t\tbg_location_list.append(location)\n\t\tfor each_fullgb in fullgb:\n\t\t\tlocation = {}\n\t\t\tlocation['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][0])\n\t\t\tlocation['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][1])\n\t\t\tfullbg_location_list.append(location)\n\n\t\turlretrieve(url = bg_url, filename = bg_filename)\n\t\tprint('缺口图片下载完成')\n\t\turlretrieve(url = fullgb_url, filename = fullbg_filename)\n\t\tprint('背景图片下载完成')\n\t\treturn bg_location_list, fullbg_location_list\n\n\tdef get_merge_image(self, filename, location_list):\n\t\t\"\"\"\n\t\t根据位置对图片进行合并还原\n\t\t:filename:图片\n\t\t:location_list:图片位置\n\t\t\"\"\"\n\t\tim = image.open(filename)\n\t\tnew_im = image.new('RGB', (260,116))\n\t\tim_list_upper=[]\n\t\tim_list_down=[]\n\n\t\tfor location in location_list:\n\t\t\tif location['y'] == -58:\n\t\t\t\tim_list_upper.append(im.crop((abs(location['x']),58,abs(location['x']) + 10, 166)))\n\t\t\tif location['y'] == 0:\n\t\t\t\tim_list_down.append(im.crop((abs(location['x']),0,abs(location['x']) + 10, 58)))\n\n\t\tnew_im = image.new('RGB', (260,116))\n\n\t\tx_offset = 0\n\t\tfor im in im_list_upper:\n\t\t\tnew_im.paste(im, (x_offset,0))\n\t\t\tx_offset += im.size[0]\n\n\t\tx_offset = 0\n\t\tfor im in im_list_down:\n\t\t\tnew_im.paste(im, (x_offset,58))\n\t\t\tx_offset += im.size[0]\n\n\t\tnew_im.save(filename)\n\n\t\treturn new_im\n\n\tdef get_merge_image(self, filename, location_list):\n\t\t\"\"\"\n\t\t根据位置对图片进行合并还原\n\t\t:filename:图片\n\t\t:location_list:图片位置\n\t\t\"\"\"\n\t\tim = image.open(filename)\n\t\tnew_im = image.new('RGB', (260,116))\n\t\tim_list_upper=[]\n\t\tim_list_down=[]\n\n\t\tfor location in location_list:\n\t\t\tif location['y']==-58:\n\t\t\t\tim_list_upper.append(im.crop((abs(location['x']),58,abs(location['x'])+10,166)))\n\t\t\tif location['y']==0:\n\t\t\t\tim_list_down.append(im.crop((abs(location['x']),0,abs(location['x'])+10,58)))\n\n\t\tnew_im = image.new('RGB', (260,116))\n\n\t\tx_offset = 0\n\t\tfor im in im_list_upper:\n\t\t\tnew_im.paste(im, (x_offset,0))\n\t\t\tx_offset += im.size[0]\n\n\t\tx_offset = 0\n\t\tfor im in im_list_down:\n\t\t\tnew_im.paste(im, (x_offset,58))\n\t\t\tx_offset += im.size[0]\n\n\t\tnew_im.save(filename)\n\n\t\treturn new_im\n\n\tdef is_pixel_equal(self, img1, img2, x, y):\n\t\t\"\"\"\n\t\t判断两个像素是否相同\n\t\t:param image1: 图片1\n\t\t:param image2: 图片2\n\t\t:param x: 位置x\n\t\t:param y: 位置y\n\t\t:return: 像素是否相同\n\t\t\"\"\"\n\t\t# 取两个图片的像素点\n\t\tpix1 = img1.load()[x, y]\n\t\tpix2 = img2.load()[x, y]\n\t\tthreshold = 60\n\t\tif (abs(pix1[0] - pix2[0] < threshold) and abs(pix1[1] - pix2[1] < threshold) and abs(pix1[2] - pix2[2] < threshold)):\n\t\t\treturn True\n\t\telse:\n\t\t\treturn False\n\n\tdef get_gap(self, img1, img2):\n\t\t\"\"\"\n\t\t获取缺口偏移量\n\t\t:param img1: 不带缺口图片\n\t\t:param img2: 带缺口图片\n\t\t:return:\n\t\t\"\"\"\n\t\tleft = 43\n\t\tfor i in range(left, img1.size[0]):\n\t\t\tfor j in range(img1.size[1]):\n\t\t\t\tif not self.is_pixel_equal(img1, img2, i, j):\n\t\t\t\t\tleft = i\n\t\t\t\t\treturn left\n\t\treturn left\t\n\n\tdef get_track(self, distance):\n\t\t\"\"\"\n\t\t根据偏移量获取移动轨迹\n\t\t:param distance: 偏移量\n\t\t:return: 移动轨迹\n\t\t\"\"\"\n\t\t# 移动轨迹\n\t\ttrack = []\n\t\t# 当前位移\n\t\tcurrent = 0\n\t\t# 减速阈值\n\t\tmid = distance * 4 / 5\n\t\t# 计算间隔\n\t\tt = 0.2\n\t\t# 初速度\n\t\tv = 0\n        \n\t\twhile current < distance:\n\t\t\tif current < mid:\n\t\t\t\t# 加速度为正2\n\t\t\t\ta = 2\n\t\t\telse:\t\n\t\t\t\t# 加速度为负3\n\t\t\t\ta = -3\n\t\t\t# 初速度v0\n\t\t\tv0 = v\n\t\t\t# 当前速度v = v0 + at\n\t\t\tv = v0 + a * t\n\t\t\t# 移动距离x = v0t + 1/2 * a * t^2\n\t\t\tmove = v0 * t + 1 / 2 * a * t * t\n\t\t\t# 当前位移\n\t\t\tcurrent += move\n\t\t\t# 加入轨迹\n\t\t\ttrack.append(round(move))\n\t\treturn track\n\n\tdef get_slider(self):\n\t\t\"\"\"\n\t\t获取滑块\n\t\t:return: 滑块对象\n\t\t\"\"\"\n\t\twhile True:\n\t\t\ttry:\n\t\t\t\tslider = self.browser.find_element_by_xpath(\"//div[@class='gt_slider_knob gt_show']\")\n\t\t\t\tbreak\n\t\t\texcept:\n\t\t\t\ttime.sleep(0.5)\n\t\treturn slider\n\n\tdef move_to_gap(self, slider, track):\n\t\t\"\"\"\n\t\t拖动滑块到缺口处\n\t\t:param slider: 滑块\n\t\t:param track: 轨迹\n\t\t:return:\n\t\t\"\"\"\n\t\tActionChains(self.browser).click_and_hold(slider).perform()\n\t\twhile track:\n\t\t\tx = random.choice(track)\n\t\t\tActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()\n\t\t\ttrack.remove(x)\n\t\ttime.sleep(0.5)\n\t\tActionChains(self.browser).release().perform()\n\n\tdef crack(self):\n\t\t# 打开浏览器\n\t\tself.open()\n\n\t\t# 保存的图片名字\n\t\tbg_filename = 'bg.jpg'\n\t\tfullbg_filename = 'fullbg.jpg'\n\n\t\t# 获取图片\n\t\tbg_location_list, fullbg_location_list = self.get_images(bg_filename, fullbg_filename)\n\n\t\t# 根据位置对图片进行合并还原\n\t\t# 方法1\n\t\t# bg_img = self.get_merge_image(bg_filename, bg_location_list)\n\t\t# fullbg_img = self.get_merge_image(fullbg_filename, fullbg_location_list)\n\t\t# 方法2\n\t\tbg_img = save_bg(self.browser)\n\t\tfull_bg_img = save_full_bg(self.browser)\n\n\t\t# 获取缺口位置\n\t\t# 方法1\n\t\t# gap = self.get_gap(fullbg_img, bg_img)\n\t\t# 方法2\n\t\tgap = self.get_gap(image.open(full_bg_img), image.open(bg_img))\n\t\tprint('缺口位置', gap)\n\n\t\ttrack = self.get_track(gap-self.BORDER)\n\t\tprint('滑动滑块')\n\t\tprint(track)\n\n\t\t# # 点按呼出缺口\n\t\t# slider = self.get_slider()\n\t\t# # 拖动滑块到缺口处\n\t\t# self.move_to_gap(slider, track)\n\nif __name__ == '__main__':\n\tprint('开始验证')\n\tcrack = Crack(u'中国移动')\n\tcrack.crack()\n\tprint('验证成功')\n"
  },
  {
    "path": "hero.py",
    "content": "#-*- coding: UTF-8 -*-\nfrom urllib.request import urlretrieve\nimport requests\nimport os\n\n\"\"\"\n函数说明:下载《英雄联盟盒子》中的英雄图片\n\nParameters:\n    url - GET请求地址，通过Fiddler抓包获取\n    header - headers信息\nReturns:\n    无\nAuthor:\n    Jack Cui\nBlog:\n    http://blog.csdn.net/c406495762\nModify:\n    2017-08-07\n\"\"\"\ndef hero_imgs_download(url, header):\n    req = requests.get(url = url, headers = header).json()\n    hero_num = len(req['list'])\n    print('一共有%d个英雄' % hero_num)\n    hero_images_path = 'hero_images'\n    for each_hero in req['list']:\n        hero_photo_url = each_hero['cover']\n        hero_name = each_hero['name'] + '.jpg'\n        filename = hero_images_path + '/' + hero_name\n        if hero_images_path not in os.listdir():\n            os.makedirs(hero_images_path)\n        urlretrieve(url = hero_photo_url, filename = filename)\n\n\"\"\"\n函数说明:打印所有英雄的名字和ID\n\nParameters:\n    url - GET请求地址，通过Fiddler抓包获取\n    header - headers信息\nReturns:\n    无\nAuthor:\n    Jack Cui\nBlog:\n    http://blog.csdn.net/c406495762\nModify:\n    2017-08-07\n\"\"\"\ndef hero_list(url, header):\n\tprint('*' * 100)\n\tprint('\\t\\t\\t\\t欢迎使用《王者荣耀》出装下助手！')\n\tprint('*' * 100)\n\treq = requests.get(url = url, headers = header).json()\n\tflag = 0\n\tfor each_hero in req['list']:\n\t\tflag += 1\n\t\tprint('%s的ID为:%-7s' % (each_hero['name'], each_hero['hero_id']), end = '\\t\\t')\n\t\tif flag == 3:\n\t\t\tprint('\\n', end = '')\n\t\t\tflag = 0\n\n\"\"\"\n函数说明:根据equip_id查询武器名字和价格\n\nParameters:\n    equip_id - 武器的ID\n    weapon_info - 存储所有武器的字典\nReturns:\n    weapon_name - 武器的名字\n    weapon_price - 武器的价格\nAuthor:\n    Jack Cui\nBlog:\n    http://blog.csdn.net/c406495762\nModify:\n    2017-08-07\n\"\"\"\ndef seek_weapon(equip_id, weapon_info):\n\tfor each_weapon in weapon_info:\n\t\tif each_weapon['equip_id'] == str(equip_id):\n\t\t\tweapon_name = each_weapon['name']\n\t\t\tweapon_price = each_weapon['price']\n\t\t\treturn weapon_name, weapon_price\n\n\n\"\"\"\n函数说明:获取并打印出装信息\n\nParameters:\n    url - GET请求地址，通过Fiddler抓包获取\n    header - headers信息\n    weapon_info - 存储所有武器的字典\nReturns:\n\t无\nAuthor:\n    Jack Cui\nBlog:\n    http://blog.csdn.net/c406495762\nModify:\n    2017-08-07\n\"\"\"\ndef hero_info(url, header, weapon_info):\n\treq = requests.get(url = url, headers = header).json()\n\tprint('\\n历史上的%s:\\n    %s' % (req['info']['name'], req['info']['history_intro']))\n\tfor each_equip_choice in req['info']['equip_choice']:\n\t\tprint('\\n%s:\\n   %s' % (each_equip_choice['title'], each_equip_choice['description']))\n\t\ttotal_price = 0\n\t\tflag = 0\n\t\tfor each_weapon in each_equip_choice['list']:\n\t\t\tflag += 1\n\t\t\tweapon_name, weapon_price = seek_weapon(each_weapon['equip_id'], weapon_info)\n\t\t\tprint('%s:%s' % (weapon_name, weapon_price), end = '\\t')\n\t\t\tif flag == 3:\n\t\t\t\tprint('\\n', end = '')\n\t\t\t\tflag = 0\n\t\t\ttotal_price += int(weapon_price)\n\t\tprint('神装套件价格共计:%d' % total_price)\n\n\n\"\"\"\n函数说明:获取武器信息\n\nParameters:\n    url - GET请求地址，通过Fiddler抓包获取\n    header - headers信息\nReturns:\n    weapon_info_dict - 武器信息\nAuthor:\n    Jack Cui\nBlog:\n    http://blog.csdn.net/c406495762\nModify:\n    2017-08-07\n\"\"\"\ndef hero_weapon(url, header):\n    req = requests.get(url = url, headers = header).json()\n    weapon_info_dict = req['list']\n    return weapon_info_dict\n\n\nif __name__ == '__main__':\n    headers = {'Accept-Charset': 'UTF-8',\n            'Accept-Encoding': 'gzip,deflate',\n            'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 6.0.1; MI 5 MIUI/V8.1.6.0.MAACNDI)',\n            'X-Requested-With': 'XMLHttpRequest',\n            'Content-type': 'application/x-www-form-urlencoded',\n            'Connection': 'Keep-Alive',\n            'Host': 'gamehelper.gm825.com'}\n    weapon_url = \"http://gamehelper.gm825.com/wzry/equip/list?channel_id=90009a&app_id=h9044j&game_id=7622&game_name=%E7%8E%8B%E8%80%85%E8%8D%A3%E8%80%80&vcode=12.0.3&version_code=1203&cuid=2654CC14D2D3894DBF5808264AE2DAD7&ovr=6.0.1&device=Xiaomi_MI+5&net_type=1&client_id=1Yfyt44QSqu7PcVdDduBYQ%3D%3D&info_ms=fBzJ%2BCu4ZDAtl4CyHuZ%2FJQ%3D%3D&info_ma=XshbgIgi0V1HxXTqixI%2BKbgXtNtOP0%2Fn1WZtMWRWj5o%3D&mno=0&info_la=9AChHTMC3uW%2BfY8%2BCFhcFw%3D%3D&info_ci=9AChHTMC3uW%2BfY8%2BCFhcFw%3D%3D&mcc=0&clientversion=&bssid=VY%2BeiuZRJ%2FwaXmoLLVUrMODX1ZTf%2F2dzsWn2AOEM0I4%3D&os_level=23&os_id=dc451556fc0eeadb&resolution=1080_1920&dpi=480&client_ip=192.168.0.198&pdunid=a83d20d8\"\n    heros_url = \"http://gamehelper.gm825.com/wzry/hero/list?channel_id=90009a&app_id=h9044j&game_id=7622&game_name=%E7%8E%8B%E8%80%85%E8%8D%A3%E8%80%80&vcode=12.0.3&version_code=1203&cuid=2654CC14D2D3894DBF5808264AE2DAD7&ovr=6.0.1&device=Xiaomi_MI+5&net_type=1&client_id=1Yfyt44QSqu7PcVdDduBYQ%3D%3D&info_ms=fBzJ%2BCu4ZDAtl4CyHuZ%2FJQ%3D%3D&info_ma=XshbgIgi0V1HxXTqixI%2BKbgXtNtOP0%2Fn1WZtMWRWj5o%3D&mno=0&info_la=9AChHTMC3uW%2BfY8%2BCFhcFw%3D%3D&info_ci=9AChHTMC3uW%2BfY8%2BCFhcFw%3D%3D&mcc=0&clientversion=&bssid=VY%2BeiuZRJ%2FwaXmoLLVUrMODX1ZTf%2F2dzsWn2AOEM0I4%3D&os_level=23&os_id=dc451556fc0eeadb&resolution=1080_1920&dpi=480&client_ip=192.168.0.198&pdunid=a83d20d8\"\n    hero_list(heros_url, headers)\n    hero_id = input(\"请输入要查询的英雄ID:\")\n    hero_url = \"http://gamehelper.gm825.com/wzry/hero/detail?hero_id={}&channel_id=90009a&app_id=h9044j&game_id=7622&game_name=%E7%8E%8B%E8%80%85%E8%8D%A3%E8%80%80&vcode=12.0.3&version_code=1203&cuid=2654CC14D2D3894DBF5808264AE2DAD7&ovr=6.0.1&device=Xiaomi_MI+5&net_type=1&client_id=1Yfyt44QSqu7PcVdDduBYQ%3D%3D&info_ms=fBzJ%2BCu4ZDAtl4CyHuZ%2FJQ%3D%3D&info_ma=XshbgIgi0V1HxXTqixI%2BKbgXtNtOP0%2Fn1WZtMWRWj5o%3D&mno=0&info_la=9AChHTMC3uW%2BfY8%2BCFhcFw%3D%3D&info_ci=9AChHTMC3uW%2BfY8%2BCFhcFw%3D%3D&mcc=0&clientversion=&bssid=VY%2BeiuZRJ%2FwaXmoLLVUrMODX1ZTf%2F2dzsWn2AOEM0I4%3D&os_level=23&os_id=dc451556fc0eeadb&resolution=1080_1920&dpi=480&client_ip=192.168.0.198&pdunid=a83d20d8\".format(hero_id)\n    weapon_info_dict = hero_weapon(weapon_url, headers)\n    hero_info(hero_url, headers, weapon_info_dict)"
  },
  {
    "path": "one_hour_spider/biquge20180731.py",
    "content": "# -*- coding:utf-8 -*-\nimport requests\nfrom bs4 import BeautifulSoup\nimport os\n\n\"\"\"\n从www.biqubao.com笔趣阁爬取小说，楼主教程中的网址我当时没打开，\n就参照楼主教程，爬取了笔趣阁小说网的内容。\n    2018-07-31\n\"\"\"\n\nif __name__=='__main__':\n    #所要爬取的小说主页，每次使用时，修改该网址即可，同时保证本地保存根路径存在即可\n    target=\"https://www.biqubao.com/book/17570/\"\n    # 本地保存爬取的文本根路径\n    save_path = 'G:/pythonlearn'\n    #笔趣阁网站根路径\n    index_path='https://www.biqubao.com'\n\n    req=requests.get(url=target)\n    #查看request默认的编码，发现与网站response不符，改为网站使用的gdk\n    print(req.encoding)\n    req.encoding = 'gbk'\n    #解析html\n    soup=BeautifulSoup(req.text,\"html.parser\")\n    list_tag=soup.div(id=\"list\")\n    print('list_tag:',list_tag)\n    #获取小说名称\n    story_title=list_tag[0].dl.dt.string\n    # 根据小说名称创建一个文件夹,如果不存在就新建\n    dir_path=save_path+'/'+story_title\n    if not os.path.exists(dir_path):\n        os.path.join(save_path,story_title)\n        os.mkdir(dir_path)\n    #开始循环每一个章节，获取章节名称，与章节对应的网址\n    for dd_tag in list_tag[0].dl.find_all('dd'):\n        #章节名称\n        chapter_name=dd_tag.string\n        #章节网址\n        chapter_url=index_path+dd_tag.a.get('href')\n        #访问该章节详情网址，爬取该章节正文\n        chapter_req = requests.get(url=chapter_url)\n        chapter_req.encoding = 'gbk'\n        chapter_soup = BeautifulSoup(chapter_req.text, \"html.parser\")\n        #解析出来正文所在的标签\n        content_tag = chapter_soup.div.find(id=\"content\")\n        #获取正文文本，并将空格替换为换行符\n        content_text = str(content_tag.text.replace('\\xa0','\\n'))\n        #将当前章节，写入以章节名字命名的txt文件\n        with open(dir_path+'/'+chapter_name+'.txt', 'w') as f:\n            f.write('本文网址:'+chapter_url)\n            f.write(content_text)"
  },
  {
    "path": "one_hour_spider/biqukan.py",
    "content": "# -*- coding:UTF-8 -*-\nfrom bs4 import BeautifulSoup\nimport requests, sys\n\n\"\"\"\n类说明:下载《笔趣看》网小说《一念永恒》\nParameters:\n\t无\nReturns:\n\t无\nModify:\n\t2017-09-13\n\"\"\"\nclass downloader(object):\n\n\tdef __init__(self):\n\t\tself.server = 'http://www.biqukan.com/'\n\t\tself.target = 'http://www.biqukan.com/1_1094/'\n\t\tself.names = []\t\t\t#存放章节名\n\t\tself.urls = []\t\t\t#存放章节链接\n\t\tself.nums = 0\t\t\t#章节数\n\n\t\"\"\"\n\t函数说明:获取下载链接\n\tParameters:\n\t\t无\n\tReturns:\n\t\t无\n\tModify:\n\t\t2017-09-13\n\t\"\"\"\n\tdef get_download_url(self):\n\t    req = requests.get(url = self.target)\n\t    html = req.text\n\t    div_bf = BeautifulSoup(html)\n\t    div = div_bf.find_all('div', class_ = 'listmain')\n\t    a_bf = BeautifulSoup(str(div[0]))\n\t    a = a_bf.find_all('a')\n\t    self.nums = len(a[15:])\t\t\t\t\t\t\t\t#剔除不必要的章节，并统计章节数\n\t    for each in a[15:]:\n\t    \tself.names.append(each.string)\n\t    \tself.urls.append(self.server + each.get('href'))\n\n\t\"\"\"\n\t函数说明:获取章节内容\n\tParameters:\n\t\ttarget - 下载连接(string)\n\tReturns:\n\t\ttexts - 章节内容(string)\n\tModify:\n\t\t2017-09-13\n\t\"\"\"\n\tdef get_contents(self, target):\n\t\treq = requests.get(url = target)\n\t\thtml = req.text\n\t\tbf = BeautifulSoup(html)\n\t\ttexts = bf.find_all('div', class_ = 'showtxt')\n\t\ttexts = texts[0].text.replace('\\xa0'*8,'\\n\\n')\n\t\treturn texts\n\n\t\"\"\"\n\t函数说明:将爬取的文章内容写入文件\n\tParameters:\n\t\tname - 章节名称(string)\n\t\tpath - 当前路径下,小说保存名称(string)\n\t\ttext - 章节内容(string)\n\tReturns:\n\t\t无\n\tModify:\n\t\t2017-09-13\n\t\"\"\"\n\tdef writer(self, name, path, text):\n\t\twrite_flag = True\n\t\twith open(path, 'a', encoding='utf-8') as f:\n\t\t\tf.write(name + '\\n')\n\t\t\tf.writelines(text)\n\t\t\tf.write('\\n\\n')\n\nif __name__ == \"__main__\":\n\tdl = downloader()\n\tdl.get_download_url()\n\tprint('《一年永恒》开始下载：')\n\tfor i in range(dl.nums):\n\t\tdl.writer(dl.names[i], '一念永恒.txt', dl.get_contents(dl.urls[i]))\n\t\tsys.stdout.write(\"  已下载:%.3f%%\" %  float(i/dl.nums*100) + '\\r')\n\t\tsys.stdout.flush()\n\tprint('《一年永恒》下载完成')\n"
  },
  {
    "path": "one_hour_spider/unsplash.py",
    "content": "# -*- coding:UTF-8 -*-\nimport requests, json, time, sys\nfrom contextlib import closing\n\nclass get_photos(object):\n\n\tdef __init__(self):\n\t\tself.photos_id = []\n\t\tself.download_server = 'https://unsplash.com/photos/xxx/download?force=trues'\n\t\tself.target = 'http://unsplash.com/napi/feeds/home'\n\t\tself.headers = {'authorization':'Client-ID c94869b36aa272dd62dfaeefed769d4115fb3189a9d1ec88ed457207747be626'}\n\n\t\"\"\"\n\t函数说明:获取图片ID\n\tParameters:\n\t\t无\n\tReturns:\n\t\t无\n\tModify:\n\t\t2017-09-13\n\t\"\"\"\t\n\tdef get_ids(self):\n\t\treq = requests.get(url=self.target, headers=self.headers, verify=False)\n\t\thtml = json.loads(req.text)\n\t\tnext_page = html['next_page']\n\t\tfor each in html['photos']:\n\t\t\tself.photos_id.append(each['id'])\n\t\ttime.sleep(1)\n\t\tfor i in range(5):\n\t\t\treq = requests.get(url=next_page, headers=self.headers, verify=False)\n\t\t\thtml = json.loads(req.text)\n\t\t\tnext_page = html['next_page']\n\t\t\tfor each in html['photos']:\n\t\t\t\tself.photos_id.append(each['id'])\n\t\t\ttime.sleep(1)\n\n\n\t\"\"\"\n\t函数说明:图片下载\n\tParameters:\n\t\t无\n\tReturns:\n\t\t无\n\tModify:\n\t\t2017-09-13\n\t\"\"\"\t\n\tdef download(self, photo_id, filename):\n\t\theaders = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'}\n\t\ttarget = self.download_server.replace('xxx', photo_id)\n\t\twith closing(requests.get(url=target, stream=True, verify = False, headers = self.headers)) as r:\n\t\t\twith open('%d.jpg' % filename, 'ab+') as f:\n\t\t\t\tfor chunk in r.iter_content(chunk_size = 1024):\n\t\t\t\t\tif chunk:\n\t\t\t\t\t\tf.write(chunk)\n\t\t\t\t\t\tf.flush()\n\nif __name__ == '__main__':\n\tgp = get_photos()\n\tprint('获取图片连接中:')\n\tgp.get_ids()\n\tprint('图片下载中:')\n\tfor i in range(len(gp.photos_id)):\n\t\tprint('  正在下载第%d张图片' % (i+1))\n\t\tgp.download(gp.photos_id[i], (i+1))"
  },
  {
    "path": "one_hour_spider/unsplash20180731.py",
    "content": "# -*- coding:utf-8 -*-\nimport requests\nimport json\nimport os\nfrom contextlib import closing\n\n\"\"\"\n从https://unsplash.com/爬取壁纸代码，使用时我是开启了代理软件\n国内网速貌似有些限制，很慢\n    2018-07-31\n\"\"\"\n\n# 本地保存图片根路径（请确保根路径存在）\nsave_path = 'G:/pythonlearn'\ndir_path=save_path+'/'+'unsplash-image'\nif not os.path.exists(dir_path):\n    os.path.join(save_path, 'unsplash-image')\n    os.mkdir(dir_path)\nn=10\n#n建议从第2页开始，因为第一页的per_page可能是1，不是12\nwhile n>2:\n    print('当前爬取第'+str(n)+'次加载图片（本次共12张）')\n    url='https://unsplash.com/napi/photos?page='+str(n)+'&per_page=12&order_by=latest'\n    req=requests.get(url=url)\n    html=json.loads(req.text)\n    for each in html:\n        downloadurl=each['links'][\"download\"]\n        jpgrep=requests.get(url=downloadurl)\n        with closing(requests.get(url=downloadurl, stream=True)) as r:\n            with open(dir_path+'/'+each['id']+'.jpg', 'ab+') as f:\n                for chunk in r.iter_content(chunk_size=1024):\n                    if chunk:\n                        f.write(chunk)\n                        f.flush()\n    n=n-1"
  },
  {
    "path": "one_hour_spider/vidoe_downloader.py",
    "content": "#-*- coding:UTF-8 -*-\nimport requests,re, json, sys\nfrom bs4 import BeautifulSoup\nfrom urllib import request\n\nclass video_downloader():\n\tdef __init__(self, url):\n\t\tself.server = 'http://api.xfsub.com'\n\t\tself.api = 'http://api.xfsub.com/xfsub_api/?url='\n\t\tself.get_url_api = 'http://api.xfsub.com/xfsub_api/url.php'\n\t\tself.url = url.split('#')[0]\n\t\tself.headers = {'Referer': 'http://api.xfsub.com/xfsub_api/?url=%s?qqdrsign=055a4' % self.url}\n\t\tself.target = self.api + self.url\n\t\tself.s = requests.session()\n\n\t\"\"\"\n\t函数说明:获取key、time、url等参数\n\tParameters:\n\t\t无\n\tReturns:\n\t\t无\n\tModify:\n\t\t2017-09-18\n\t\"\"\"\n\tdef get_key(self):\n\t\treq = self.s.get(url=self.target)\n\t\treq.encoding = 'utf-8'\n\t\tself.info = json.loads(re.findall('\"url.php\",\\ (.+),', req.text)[0])\t#使用正则表达式匹配结果，将匹配的结果存入info变量中\n\n\t\"\"\"\n\t函数说明:获取视频地址\n\tParameters:\n\t\t无\n\tReturns:\n\t\tvideo_url - 视频存放地址\n\tModify:\n\t\t2017-09-18\n\t\"\"\"\n\tdef get_url(self):\n\t\tdata = {'time':self.info['time'],\n\t\t\t'key':self.info['key'],\n\t\t\t'url':self.info['url'],\n\t\t\t'type':''}\n\t\treq = self.s.post(url=self.get_url_api,data=data, headers=self.headers)\n\t\turl = self.server + json.loads(req.text)['url']\n\t\treq = self.s.get(url=url, headers=self.headers)\n\t\tbf = BeautifulSoup(req.text,'xml')\t\t\t\t\t\t\t\t\t\t#因为文件是xml格式的，所以要进行xml解析。\n\t\tvideo_url = bf.find('file').string\t\t\t\t\t\t\t\t\t\t#匹配到视频地址\n\t\treturn video_url\n\n\t\"\"\"\n\t函数说明:回调函数，打印下载进度\n\tParameters:\n\t\ta b c - 返回信息\n\tReturns:\n\t\t无\n\tModify:\n\t\t2017-09-18\n\t\"\"\"\n\tdef Schedule(self, a, b, c):\n\t\tper = 100.0*a*b/c\n\t\tif per > 100 :\n\t\t\tper = 1\n\t\tsys.stdout.write(\"  \" + \"%.2f%% 已经下载的大小:%ld 文件大小:%ld\" % (per,a*b,c) + '\\r')\n\t\tsys.stdout.flush()\n\n\t\"\"\"\n\t函数说明:视频下载\n\tParameters:\n\t\turl - 视频地址\n\t\tfilename - 视频名字\n\tReturns:\n\t\t无\n\tModify:\n\t\t2017-09-18\n\t\"\"\"\n\tdef video_download(self, url, filename):\n\t\trequest.urlretrieve(url=url,filename=filename,reporthook=self.Schedule)\n\n\nif __name__ == '__main__':\n\turl = 'http://www.iqiyi.com/v_19rr7qhfg0.html#vfrm=19-9-0-1'\n\tvd = video_downloader(url)\n\tfilename = '加勒比海盗5'\n\tprint('%s下载中:' % filename)\n\tvd.get_key()\n\tvideo_url = vd.get_url()\n\tprint('  获取地址成功:%s' % video_url)\n\tvd.video_download(video_url, filename+'.mp4')\n\tprint('\\n下载完成！')\n"
  },
  {
    "path": "shuaia.py",
    "content": "# -*- coding:UTF-8 -*-\nfrom bs4 import BeautifulSoup\nfrom urllib.request import urlretrieve\nimport requests\nimport os\nimport time\n\nif __name__ == '__main__':\n\tlist_url = []\n\tfor num in range(1,3):\n\t\tif num == 1:\n\t\t\turl = 'http://www.shuaia.net/index.html'\n\t\telse:\n\t\t\turl = 'http://www.shuaia.net/index_%d.html' % num\n\t\theaders = {\n\t\t\t\t\"User-Agent\":\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36\"\n\t\t}\n\t\treq = requests.get(url = url,headers = headers)\n\t\treq.encoding = 'utf-8'\n\t\thtml = req.text\n\t\tbf = BeautifulSoup(html, 'lxml')\n\t\ttargets_url = bf.find_all(class_='item-img')\n\t\t\n\t\tfor each in targets_url:\n\t\t\tlist_url.append(each.img.get('alt') + '=' + each.get('href'))\n\n\tprint('连接采集完成')\n\n\tfor each_img in list_url:\n\t\timg_info = each_img.split('=')\n\t\ttarget_url = img_info[1]\n\t\tfilename = img_info[0] + '.jpg'\n\t\tprint('下载：' + filename)\n\t\theaders = {\n\t\t\t\"User-Agent\":\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36\"\n\t\t}\n\t\timg_req = requests.get(url = target_url,headers = headers)\n\t\timg_req.encoding = 'utf-8'\n\t\timg_html = img_req.text\n\t\timg_bf_1 = BeautifulSoup(img_html, 'lxml')\n\t\timg_url = img_bf_1.find_all('div', class_='wr-single-content-list')\n\t\timg_bf_2 = BeautifulSoup(str(img_url), 'lxml')\n\t\timg_url = 'http://www.shuaia.net' + img_bf_2.div.img.get('src')\n\t\tif 'images' not in os.listdir():\n\t\t\tos.makedirs('images')\n\t\turlretrieve(url = img_url,filename = 'images/' + filename)\n\t\ttime.sleep(1)\n\n\tprint('下载完成！')"
  },
  {
    "path": "video_downloader/MyQR/__init__.py",
    "content": ""
  },
  {
    "path": "video_downloader/MyQR/mylibs/ECC.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom MyQR.mylibs.constant import GP_list, ecc_num_per_block, lindex, po2, log\n \n#ecc: Error Correction Codewords\ndef encode(ver, ecl, data_codewords):\n    en = ecc_num_per_block[ver-1][lindex[ecl]]\n    ecc = []\n    for dc in data_codewords:\n        ecc.append(get_ecc(dc, en))\n    return ecc\n\ndef get_ecc(dc, ecc_num):\n    gp = GP_list[ecc_num]\n    remainder = dc\n    for i in range(len(dc)):\n        remainder = divide(remainder, *gp)\n    return remainder\n    \ndef divide(MP, *GP):\n    if MP[0]:\n        GP = list(GP)\n        for i in range(len(GP)):\n            GP[i] += log[MP[0]]\n            if GP[i] > 255:\n                GP[i] %= 255\n            GP[i] = po2[GP[i]]\n        return XOR(GP, *MP)\n    else:\n        return XOR([0]*len(GP), *MP)\n    \n    \ndef XOR(GP, *MP):\n    MP = list(MP)\n    a = len(MP) - len(GP)\n    if a < 0:\n        MP += [0] * (-a)\n    elif a > 0:\n        GP += [0] * a\n    \n    remainder = []\n    for i in range(1, len(MP)):\n        remainder.append(MP[i]^GP[i])\n    return remainder"
  },
  {
    "path": "video_downloader/MyQR/mylibs/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "video_downloader/MyQR/mylibs/constant.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\n***** for data.py *******\n\"\"\"\n# character capacities\n# {level1: [version1(mode1,mode2,mode3,mode4), version2(..,..,..,..), ...],\n#   level2: [version1(mode1,mode2,mode3,mode4), version2(..,..,..,..),...],\n#   ...}\nchar_cap = {\n        'L': [(41, 25, 17, 10), (77, 47, 32, 20), (127, 77, 53, 32), (187, 114, 78, 48), (255, 154, 106, 65), (322, 195, 134, 82), (370, 224, 154, 95), (461, 279, 192, 118), (552, 335, 230, 141), (652, 395, 271, 167), (772, 468, 321, 198), (883, 535, 367, 226), (1022, 619, 425, 262), (1101, 667, 458, 282), (1250, 758, 520, 320), (1408, 854, 586, 361), (1548, 938, 644, 397), (1725, 1046, 718, 442), (1903, 1153, 792, 488), (2061, 1249, 858, 528), (2232, 1352, 929, 572), (2409, 1460, 1003, 618), (2620, 1588, 1091, 672), (2812, 1704, 1171, 721), (3057, 1853, 1273, 784), (3283, 1990, 1367, 842), (3517, 2132, 1465, 902), (3669, 2223, 1528, 940), (3909, 2369, 1628, 1002), (4158, 2520, 1732, 1066), (4417, 2677, 1840, 1132), (4686, 2840, 1952, 1201), (4965, 3009, 2068, 1273), (5253, 3183, 2188, 1347), (5529, 3351, 2303, 1417), (5836, 3537, 2431, 1496), (6153, 3729, 2563, 1577), (6479, 3927, 2699, 1661), (6743, 4087, 2809, 1729), (7089, 4296, 2953, 1817)],\n        'M': [(34, 20, 14, 8), (63, 38, 26, 16), (101, 61, 42, 26), (149, 90, 62, 38), (202, 122, 84, 52), (255, 154, 106, 65), (293, 178, 122, 75), (365, 221, 152, 93), (432, 262, 180, 111), (513, 311, 213, 131), (604, 366, 251, 155), (691, 419, 287, 177), (796, 483, 331, 204), (871, 528, 362, 223), (991, 600, 412, 254), (1082, 656, 450, 277), (1212, 734, 504, 310), (1346, 816, 560, 345), (1500, 909, 624, 384), (1600, 970, 666, 410), (1708, 1035, 711, 438), (1872, 1134, 779, 480), (2059, 1248, 857, 528), (2188, 1326, 911, 561), (2395, 1451, 997, 614), (2544, 1542, 1059, 652), (2701, 1637, 1125, 692), (2857, 1732, 1190, 732), (3035, 1839, 1264, 778), (3289, 1994, 1370, 843), (3486, 2113, 1452, 894), (3693, 2238, 1538, 947), (3909, 2369, 1628, 1002), (4134, 2506, 1722, 1060), (4343, 2632, 1809, 1113), (4588, 2780, 1911, 1176), (4775, 2894, 1989, 1224), (5039, 3054, 2099, 1292), (5313, 3220, 2213, 1362), (5596, 3391, 2331, 1435)],\n        'Q': [(27, 16, 11, 7), (48, 29, 20, 12), (77, 47, 32, 20), (111, 67, 46, 28), (144, 87, 60, 37), (178, 108, 74, 45), (207, 125, 86, 53), (259, 157, 108, 66), (312, 189, 130, 80), (364, 221, 151, 93), (427, 259, 177, 109), (489, 296, 203, 125), (580, 352, 241, 149), (621, 376, 258, 159), (703, 426, 292, 180), (775, 470, 322, 198), (876, 531, 364, 224), (948, 574, 394, 243), (1063, 644, 442, 272), (1159, 702, 482, 297), (1224, 742, 509, 314), (1358, 823, 565, 348), (1468, 890, 611, 376), (1588, 963, 661, 407), (1718, 1041, 715, 440), (1804, 1094, 751, 462), (1933, 1172, 805, 496), (2085, 1263, 868, 534), (2181, 1322, 908, 559), (2358, 1429, 982, 604), (2473, 1499, 1030, 634), (2670, 1618, 1112, 684), (2805, 1700, 1168, 719), (2949, 1787, 1228, 756), (3081, 1867, 1283, 790), (3244, 1966, 1351, 832), (3417, 2071, 1423, 876), (3599, 2181, 1499, 923), (3791, 2298, 1579, 972), (3993, 2420, 1663, 1024)],\n        'H': [(17, 10, 7, 4), (34, 20, 14, 8), (58, 35, 24, 15), (82, 50, 34, 21), (106, 64, 44, 27), (139, 84, 58, 36), (154, 93, 64, 39), (202, 122, 84, 52), (235, 143, 98, 60), (288, 174, 119, 74), (331, 200, 137, 85), (374, 227, 155, 96), (427, 259, 177, 109), (468, 283, 194, 120), (530, 321, 220, 136), (602, 365, 250, 154), (674, 408, 280, 173), (746, 452, 310, 191), (813, 493, 338, 208), (919, 557, 382, 235), (969, 587, 403, 248), (1056, 640, 439, 270), (1108, 672, 461, 284), (1228, 744, 511, 315), (1286, 779, 535, 330), (1425, 864, 593, 365), (1501, 910, 625, 385), (1581, 958, 658, 405), (1677, 1016, 698, 430), (1782, 1080, 742, 457), (1897, 1150, 790, 486), (2022, 1226, 842, 518), (2157, 1307, 898, 553), (2301, 1394, 958, 590), (2361, 1431, 983, 605), (2524, 1530, 1051, 647), (2625, 1591, 1093, 673), (2735, 1658, 1139, 701), (2927, 1774, 1219, 750), (3057, 1852, 1273, 784)]\n        }\n\nmindex = {'numeric':0, 'alphanumeric':1, 'byte':2, 'kanji':3}\n\n# [\n# version1[level1,level2,level3,level4], \n# version2[..,..,..,..],\n# ...\n#   ]\nrequired_bytes = [\n        [19, 16, 13, 9], [34, 28, 22, 16], [55, 44, 34, 26], [80, 64, 48, 36], [108, 86, 62, 46], [136, 108, 76, 60], [156, 124, 88, 66], [194, 154, 110, 86], [232, 182, 132, 100], [274, 216, 154, 122], [324, 254, 180, 140], [370, 290, 206, 158], [428, 334, 244, 180], [461, 365, 261, 197], [523, 415, 295, 223], [589, 453, 325, 253], [647, 507, 367, 283], [721, 563, 397, 313], [795, 627, 445, 341], [861, 669, 485, 385], [932, 714, 512, 406], [1006, 782, 568, 442], [1094, 860, 614, 464], [1174, 914, 664, 514], [1276, 1000, 718, 538], [1370, 1062, 754, 596], [1468, 1128, 808, 628], [1531, 1193, 871, 661], [1631, 1267, 911, 701], [1735, 1373, 985, 745], [1843, 1455, 1033, 793], [1955, 1541, 1115, 845], [2071, 1631, 1171, 901], [2191, 1725, 1231, 961], [2306, 1812, 1286, 986], [2434, 1914, 1354, 1054], [2566, 1992, 1426, 1096], [2702, 2102, 1502, 1142], [2812, 2216, 1582, 1222], [2956, 2334, 1666, 1276]\n        ]\n\nnum_list = '0123456789'\nalphanum_list = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'\n\n# [\n# version1[\n#       level1(num_of_group_1_blocks, DC_per_group_1_block, num_of_group_2_blocks, DC_per_group_2_block),\n#       level2(..,..,..,..),\n#       level3(..,..,..,..),\n#       level4(..,..,..,..)\n#       ], \n# version2[level1(..), level2(..), level3(..), level4(..)],\n# ...\n#   ]\ngrouping_list = [\n    [(1, 19, 0, 0), (1, 16, 0, 0), (1, 13, 0, 0), (1, 9, 0, 0)], [(1, 34, 0, 0), (1, 28, 0, 0), (1, 22, 0, 0), (1, 16, 0, 0)], [(1, 55, 0, 0), (1, 44, 0, 0), (2, 17, 0, 0), (2, 13, 0, 0)], [(1, 80, 0, 0), (2, 32, 0, 0), (2, 24, 0, 0), (4, 9, 0, 0)], [(1, 108, 0, 0), (2, 43, 0, 0), (2, 15, 2, 16), (2, 11, 2, 12)], [(2, 68, 0, 0), (4, 27, 0, 0), (4, 19, 0, 0), (4, 15, 0, 0)], [(2, 78, 0, 0), (4, 31, 0, 0), (2, 14, 4, 15), (4, 13, 1, 14)], [(2, 97, 0, 0), (2, 38, 2, 39), (4, 18, 2, 19), (4, 14, 2, 15)], [(2, 116, 0, 0), (3, 36, 2, 37), (4, 16, 4, 17), (4, 12, 4, 13)], [(2, 68, 2, 69), (4, 43, 1, 44), (6, 19, 2, 20), (6, 15, 2, 16)], [(4, 81, 0, 0), (1, 50, 4, 51), (4, 22, 4, 23), (3, 12, 8, 13)], [(2, 92, 2, 93), (6, 36, 2, 37), (4, 20, 6, 21), (7, 14, 4, 15)], [(4, 107, 0, 0), (8, 37, 1, 38), (8, 20, 4, 21), (12, 11, 4, 12)], [(3, 115, 1, 116), (4, 40, 5, 41), (11, 16, 5, 17), (11, 12, 5, 13)], [(5, 87, 1, 88), (5, 41, 5, 42), (5, 24, 7, 25), (11, 12, 7, 13)], [(5, 98, 1, 99), (7, 45, 3, 46), (15, 19, 2, 20), (3, 15, 13, 16)], [(1, 107, 5, 108), (10, 46, 1, 47), (1, 22, 15, 23), (2, 14, 17, 15)], [(5, 120, 1, 121), (9, 43, 4, 44), (17, 22, 1, 23), (2, 14, 19, 15)], [(3, 113, 4, 114), (3, 44, 11, 45), (17, 21, 4, 22), (9, 13, 16, 14)], [(3, 107, 5, 108), (3, 41, 13, 42), (15, 24, 5, 25), (15, 15, 10, 16)], [(4, 116, 4, 117), (17, 42, 0, 0), (17, 22, 6, 23), (19, 16, 6, 17)], [(2, 111, 7, 112), (17, 46, 0, 0), (7, 24, 16, 25), (34, 13, 0, 0)], [(4, 121, 5, 122), (4, 47, 14, 48), (11, 24, 14, 25), (16, 15, 14, 16)], [(6, 117, 4, 118), (6, 45, 14, 46), (11, 24, 16, 25), (30, 16, 2, 17)], [(8, 106, 4, 107), (8, 47, 13, 48), (7, 24, 22, 25), (22, 15, 13, 16)], [(10, 114, 2, 115), (19, 46, 4, 47), (28, 22, 6, 23), (33, 16, 4, 17)], [(8, 122, 4, 123), (22, 45, 3, 46), (8, 23, 26, 24), (12, 15, 28, 16)], [(3, 117, 10, 118), (3, 45, 23, 46), (4, 24, 31, 25), (11, 15, 31, 16)], [(7, 116, 7, 117), (21, 45, 7, 46), (1, 23, 37, 24), (19, 15, 26, 16)], [(5, 115, 10, 116), (19, 47, 10, 48), (15, 24, 25, 25), (23, 15, 25, 16)], [(13, 115, 3, 116), (2, 46, 29, 47), (42, 24, 1, 25), (23, 15, 28, 16)], [(17, 115, 0, 0), (10, 46, 23, 47), (10, 24, 35, 25), (19, 15, 35, 16)], [(17, 115, 1, 116), (14, 46, 21, 47), (29, 24, 19, 25), (11, 15, 46, 16)], [(13, 115, 6, 116), (14, 46, 23, 47), (44, 24, 7, 25), (59, 16, 1, 17)], [(12, 121, 7, 122), (12, 47, 26, 48), (39, 24, 14, 25), (22, 15, 41, 16)], [(6, 121, 14, 122), (6, 47, 34, 48), (46, 24, 10, 25), (2, 15, 64, 16)], [(17, 122, 4, 123), (29, 46, 14, 47), (49, 24, 10, 25), (24, 15, 46, 16)], [(4, 122, 18, 123), (13, 46, 32, 47), (48, 24, 14, 25), (42, 15, 32, 16)], [(20, 117, 4, 118), (40, 47, 7, 48), (43, 24, 22, 25), (10, 15, 67, 16)], [(19, 118, 6, 119), (18, 47, 31, 48), (34, 24, 34, 25), (20, 15, 61, 16)]\n    ]\n\nmode_indicator = {'numeric': '0001', 'alphanumeric': '0010', 'byte': '0100', 'kanji': '1000'}\n\n\n\n\"\"\"\n******  for ECC.py  *******\n\"\"\"\n#GP: Generator Polynomial, MP: Message Polynomial\nGP_list = {\n        7: [0, 87, 229, 146, 149, 238, 102, 21],\n        10: [0, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45],\n        13: [0, 74, 152, 176, 100, 86, 100, 106, 104, 130, 218, 206, 140, 78],\n        15: [0, 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105],\n        16: [0, 120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, 225, 120],\n        17: [0, 43, 139, 206, 78, 43, 239, 123, 206, 214, 147, 24, 99, 150, 39, 243, 163, 136],\n        18: [0, 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153],\n        20: [0, 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190],\n        22: [0, 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, 219, 134, 160, 105, 165, 231],\n        24: [0, 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 0, 117, 232, 87, 96, 227, 21],\n        26: [0, 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, 245, 142, 13, 102, 48, 227, 153, 145, 218, 70],\n        28: [0, 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 21, 43, 245, 87, 42, 195, 212, 119, 242, 37, 9, 123],\n        30: [0, 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, 180]\n        }\n\n# Error Correction Codewords per block\n# [version1(level1,level2,level3,level4),\n#  version2(..,..,..,..),\n#   ....]\necc_num_per_block = [\n    (7, 10, 13, 17), (10, 16, 22, 28), (15, 26, 18, 22), (20, 18, 26, 16), (26, 24, 18, 22), (18, 16, 24, 28), (20, 18, 18, 26), (24, 22, 22, 26), (30, 22, 20, 24), (18, 26, 24, 28), (20, 30, 28, 24), (24, 22, 26, 28), (26, 22, 24, 22), (30, 24, 20, 24), (22, 24, 30, 24), (24, 28, 24, 30), (28, 28, 28, 28), (30, 26, 28, 28), (28, 26, 26, 26), (28, 26, 30, 28), (28, 26, 28, 30), (28, 28, 30, 24), (30, 28, 30, 30), (30, 28, 30, 30), (26, 28, 30, 30), (28, 28, 28, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30), (30, 28, 30, 30)\n    ]\n        \n\n# powers of 2 list  \npo2 = [\n    1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1\n    ]\n    \n# log list\nlog = [\n    None, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175\n    ]\n\n\"\"\"\n******  for data.py + ECC.py + structure.py + matrix.py  *******\n\"\"\"\nlindex = {'L':0, 'M':1, 'Q':2, 'H':3}\n    \n\"\"\"\n******  for structure.py  *******\n\"\"\"    \nrequired_remainder_bits = (0,7,7,7,7,7,0,0,0,0,0,0,0,3,3,3,3,3,3,3,4,4,4,4,4,4,4,3,3,3,3,3,3,3,0,0,0,0,0,0)\n\n# [\n# version1[\n#       level1(num_of_group_1_blocks, DC_per_group_1_block, num_of_group_2_blocks, DC_per_group_2_block),\n#       level2(..,..,..,..),\n#       level3(..,..,..,..),\n#       level4(..,..,..,..)\n#       ], \n# version2[level1(..), level2(..), level3(..), level4(..)],\n# ...\n#   ]\ngrouping_list = [\n    [(1, 19, 0, 0), (1, 16, 0, 0), (1, 13, 0, 0), (1, 9, 0, 0)], [(1, 34, 0, 0), (1, 28, 0, 0), (1, 22, 0, 0), (1, 16, 0, 0)], [(1, 55, 0, 0), (1, 44, 0, 0), (2, 17, 0, 0), (2, 13, 0, 0)], [(1, 80, 0, 0), (2, 32, 0, 0), (2, 24, 0, 0), (4, 9, 0, 0)], [(1, 108, 0, 0), (2, 43, 0, 0), (2, 15, 2, 16), (2, 11, 2, 12)], [(2, 68, 0, 0), (4, 27, 0, 0), (4, 19, 0, 0), (4, 15, 0, 0)], [(2, 78, 0, 0), (4, 31, 0, 0), (2, 14, 4, 15), (4, 13, 1, 14)], [(2, 97, 0, 0), (2, 38, 2, 39), (4, 18, 2, 19), (4, 14, 2, 15)], [(2, 116, 0, 0), (3, 36, 2, 37), (4, 16, 4, 17), (4, 12, 4, 13)], [(2, 68, 2, 69), (4, 43, 1, 44), (6, 19, 2, 20), (6, 15, 2, 16)], [(4, 81, 0, 0), (1, 50, 4, 51), (4, 22, 4, 23), (3, 12, 8, 13)], [(2, 92, 2, 93), (6, 36, 2, 37), (4, 20, 6, 21), (7, 14, 4, 15)], [(4, 107, 0, 0), (8, 37, 1, 38), (8, 20, 4, 21), (12, 11, 4, 12)], [(3, 115, 1, 116), (4, 40, 5, 41), (11, 16, 5, 17), (11, 12, 5, 13)], [(5, 87, 1, 88), (5, 41, 5, 42), (5, 24, 7, 25), (11, 12, 7, 13)], [(5, 98, 1, 99), (7, 45, 3, 46), (15, 19, 2, 20), (3, 15, 13, 16)], [(1, 107, 5, 108), (10, 46, 1, 47), (1, 22, 15, 23), (2, 14, 17, 15)], [(5, 120, 1, 121), (9, 43, 4, 44), (17, 22, 1, 23), (2, 14, 19, 15)], [(3, 113, 4, 114), (3, 44, 11, 45), (17, 21, 4, 22), (9, 13, 16, 14)], [(3, 107, 5, 108), (3, 41, 13, 42), (15, 24, 5, 25), (15, 15, 10, 16)], [(4, 116, 4, 117), (17, 42, 0, 0), (17, 22, 6, 23), (19, 16, 6, 17)], [(2, 111, 7, 112), (17, 46, 0, 0), (7, 24, 16, 25), (34, 13, 0, 0)], [(4, 121, 5, 122), (4, 47, 14, 48), (11, 24, 14, 25), (16, 15, 14, 16)], [(6, 117, 4, 118), (6, 45, 14, 46), (11, 24, 16, 25), (30, 16, 2, 17)], [(8, 106, 4, 107), (8, 47, 13, 48), (7, 24, 22, 25), (22, 15, 13, 16)], [(10, 114, 2, 115), (19, 46, 4, 47), (28, 22, 6, 23), (33, 16, 4, 17)], [(8, 122, 4, 123), (22, 45, 3, 46), (8, 23, 26, 24), (12, 15, 28, 16)], [(3, 117, 10, 118), (3, 45, 23, 46), (4, 24, 31, 25), (11, 15, 31, 16)], [(7, 116, 7, 117), (21, 45, 7, 46), (1, 23, 37, 24), (19, 15, 26, 16)], [(5, 115, 10, 116), (19, 47, 10, 48), (15, 24, 25, 25), (23, 15, 25, 16)], [(13, 115, 3, 116), (2, 46, 29, 47), (42, 24, 1, 25), (23, 15, 28, 16)], [(17, 115, 0, 0), (10, 46, 23, 47), (10, 24, 35, 25), (19, 15, 35, 16)], [(17, 115, 1, 116), (14, 46, 21, 47), (29, 24, 19, 25), (11, 15, 46, 16)], [(13, 115, 6, 116), (14, 46, 23, 47), (44, 24, 7, 25), (59, 16, 1, 17)], [(12, 121, 7, 122), (12, 47, 26, 48), (39, 24, 14, 25), (22, 15, 41, 16)], [(6, 121, 14, 122), (6, 47, 34, 48), (46, 24, 10, 25), (2, 15, 64, 16)], [(17, 122, 4, 123), (29, 46, 14, 47), (49, 24, 10, 25), (24, 15, 46, 16)], [(4, 122, 18, 123), (13, 46, 32, 47), (48, 24, 14, 25), (42, 15, 32, 16)], [(20, 117, 4, 118), (40, 47, 7, 48), (43, 24, 22, 25), (10, 15, 67, 16)], [(19, 118, 6, 119), (18, 47, 31, 48), (34, 24, 34, 25), (20, 15, 61, 16)]\n    ]\n\n\n\n\"\"\"\n******  for matrix.py  *******\n\"\"\"\n# Alignment Pattern Locations\nalig_location = [\n    (6, 18), (6, 22), (6, 26), (6, 30), (6, 34), (6, 22, 38), (6, 24, 42), (6, 26, 46), (6, 28, 50), (6, 30, 54), (6, 32, 58), (6, 34, 62), (6, 26, 46, 66), (6, 26, 48, 70), (6, 26, 50, 74), (6, 30, 54, 78), (6, 30, 56, 82), (6, 30, 58, 86), (6, 34, 62, 90), (6, 28, 50, 72, 94), (6, 26, 50, 74, 98), (6, 30, 54, 78, 102), (6, 28, 54, 80, 106), (6, 32, 58, 84, 110), (6, 30, 58, 86, 114), (6, 34, 62, 90, 118), (6, 26, 50, 74, 98, 122), (6, 30, 54, 78, 102, 126), (6, 26, 52, 78, 104, 130), (6, 30, 56, 82, 108, 134), (6, 34, 60, 86, 112, 138), (6, 30, 58, 86, 114, 142), (6, 34, 62, 90, 118, 146), (6, 30, 54, 78, 102, 126, 150), (6, 24, 50, 76, 102, 128, 154), (6, 28, 54, 80, 106, 132, 158), (6, 32, 58, 84, 110, 136, 162), (6, 26, 54, 82, 110, 138, 166), (6, 30, 58, 86, 114, 142, 170)\n    ]\n\n# List of all Format Information Strings\n# [\n#   level1[mask_pattern0, mask_pattern1, mask_...3,...], \n#   level2[...], \n#   level3[...], \n#   level4[...]\n#   ]    \nformat_info_str = [\n    ['111011111000100', '111001011110011', '111110110101010', '111100010011101', '110011000101111', '110001100011000', '110110001000001', '110100101110110'], ['101010000010010', '101000100100101', '101111001111100', '101101101001011', '100010111111001', '100000011001110', '100111110010111', '100101010100000'], ['011010101011111', '011000001101000', '011111100110001', '011101000000110', '010010010110100', '010000110000011', '010111011011010', '010101111101101'], ['001011010001001', '001001110111110', '001110011100111', '001100111010000', '000011101100010', '000001001010101', '000110100001100', '000100000111011']\n    ]\n\n# Version Information Strings\nversion_info_str = [\n    '000111110010010100', '001000010110111100', '001001101010011001', '001010010011010011', '001011101111110110', '001100011101100010', '001101100001000111', '001110011000001101', '001111100100101000', '010000101101111000', '010001010001011101', '010010101000010111', '010011010100110010', '010100100110100110', '010101011010000011', '010110100011001001', '010111011111101100', '011000111011000100', '011001000111100001', '011010111110101011', '011011000010001110', '011100110000011010', '011101001100111111', '011110110101110101', '011111001001010000', '100000100111010101', '100001011011110000', '100010100010111010', '100011011110011111', '100100101100001011', '100101010000101110', '100110101001100100', '100111010101000001', '101000110001101001'\n    ]\n"
  },
  {
    "path": "video_downloader/MyQR/mylibs/data.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom MyQR.mylibs.constant import char_cap, required_bytes, mindex, lindex, num_list, alphanum_list, grouping_list, mode_indicator\n       \n# ecl: Error Correction Level(L,M,Q,H)\ndef encode(ver, ecl, str):\n    mode_encoding = {\n            'numeric': numeric_encoding,\n            'alphanumeric': alphanumeric_encoding,\n            'byte': byte_encoding,\n            'kanji': kanji_encoding\n            }\n          \n    ver, mode = analyse(ver, ecl, str)\n    \n    # print('line 16: mode:', mode)\n    \n    code = mode_indicator[mode] + get_cci(ver, mode, str) + mode_encoding[mode](str)\n    \n    # Add a Terminator\n    rqbits = 8 * required_bytes[ver-1][lindex[ecl]]\n    b = rqbits - len(code)\n    code += '0000' if b >= 4 else '0' * b\n    \n    # Make the Length a Multiple of 8\n    while len(code) % 8 != 0:\n        code += '0'\n    \n    # Add Pad Bytes if the String is Still too Short\n    while len(code) < rqbits:    \n        code += '1110110000010001' if rqbits - len(code) >= 16 else '11101100'\n        \n    data_code = [code[i:i+8] for i in range(len(code)) if i%8 == 0]\n    data_code = [int(i,2) for i in data_code]\n\n    g = grouping_list[ver-1][lindex[ecl]]\n    data_codewords, i = [], 0\n    for n in range(g[0]):\n        data_codewords.append(data_code[i:i+g[1]])\n        i += g[1]\n    for n in range(g[2]):\n        data_codewords.append(data_code[i:i+g[3]])\n        i += g[3]\n    \n    return ver, data_codewords\n    \ndef analyse(ver, ecl, str):\n    if all(i in num_list for i in str):\n        mode = 'numeric'\n    elif all(i in alphanum_list for i in str):\n        mode = 'alphanumeric'\n    else:\n        mode = 'byte'\n    \n    m = mindex[mode]\n    l = len(str)\n    for i in range(40):\n        if char_cap[ecl][i][m] > l:\n            ver = i + 1 if i+1 > ver else ver\n            break\n \n    return ver, mode\n\ndef numeric_encoding(str):   \n    str_list = [str[i:i+3] for i in range(0,len(str),3)]\n    code = ''\n    for i in str_list:\n        rqbin_len = 10\n        if len(i) == 1: \n            rqbin_len = 4\n        elif len(i) == 2:\n            rqbin_len = 7\n        code_temp = bin(int(i))[2:]\n        code += ('0'*(rqbin_len - len(code_temp)) + code_temp)\n    return code\n    \ndef alphanumeric_encoding(str):\n    code_list = [alphanum_list.index(i) for i in str]\n    code = ''\n    for i in range(1, len(code_list), 2):\n        c = bin(code_list[i-1] * 45 + code_list[i])[2:]\n        c = '0'*(11-len(c)) + c\n        code += c\n    if i != len(code_list) - 1:\n        c = bin(code_list[-1])[2:]\n        c = '0'*(6-len(c)) + c\n        code += c\n    \n    return code\n    \ndef byte_encoding(str):\n    code = ''\n    for i in str:\n        c = bin(ord(i.encode('iso-8859-1')))[2:]\n        c = '0'*(8-len(c)) + c\n        code += c\n    return code\n    \ndef kanji_encoding(str):\n    pass\n    \n# cci: character count indicator  \ndef get_cci(ver, mode, str):\n    if 1 <= ver <= 9:\n        cci_len = (10, 9, 8, 8)[mindex[mode]]\n    elif 10 <= ver <= 26:\n        cci_len = (12, 11, 16, 10)[mindex[mode]]\n    else:\n        cci_len = (14, 13, 16, 12)[mindex[mode]]\n        \n    cci = bin(len(str))[2:]\n    cci = '0' * (cci_len - len(cci)) + cci\n    return cci\n    \nif __name__ == '__main__':\n    s = '123456789'\n    v, datacode = encode(1, 'H', s)\n    print(v, datacode)"
  },
  {
    "path": "video_downloader/MyQR/mylibs/draw.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom PIL import Image\nimport os\n\ndef draw_qrcode(abspath, qrmatrix):\n    unit_len = 3\n    x = y = 4*unit_len\n    pic = Image.new('1', [(len(qrmatrix)+8)*unit_len]*2, 'white')\n    \n    for line in qrmatrix:\n        for module in line:\n            if module:\n                draw_a_black_unit(pic, x, y, unit_len)\n            x += unit_len\n        x, y = 4*unit_len, y+unit_len\n\n    saving = os.path.join(abspath, 'qrcode.png')\n    pic.save(saving)\n    return saving\n    \ndef draw_a_black_unit(p, x, y, ul):\n    for i in range(ul):\n        for j in range(ul):\n            p.putpixel((x+i, y+j), 0)"
  },
  {
    "path": "video_downloader/MyQR/mylibs/matrix.py",
    "content": "# -*- coding: utf-8 -*-\n     \nfrom MyQR.mylibs.constant import alig_location, format_info_str, version_info_str, lindex\n    \ndef get_qrmatrix(ver, ecl, bits):\n    num = (ver - 1) * 4 + 21\n    qrmatrix = [[None] * num for i in range(num)]\n    #  [([None] * num * num)[i:i+num] for i in range(num * num) if i % num == 0] \n\n    # Add the Finder Patterns & Add the Separators\n    add_finder_and_separator(qrmatrix)\n    \n    # Add the Alignment Patterns\n    add_alignment(ver, qrmatrix)\n    \n    # Add the Timing Patterns\n    add_timing(qrmatrix)\n    \n    # Add the Dark Module and Reserved Areas\n    add_dark_and_reserving(ver, qrmatrix)\n    \n    maskmatrix = [i[:] for i in qrmatrix]\n    \n    # Place the Data Bits\n    place_bits(bits, qrmatrix)\n    \n    # Data Masking\n    mask_num, qrmatrix = mask(maskmatrix, qrmatrix)\n    \n    # Format Information\n    add_format_and_version_string(ver, ecl, mask_num, qrmatrix)\n\n    return qrmatrix\n\ndef add_finder_and_separator(m):             \n    for i in range(8):\n        for j in range(8):\n            if i in (0, 6):\n                m[i][j] = m[-i-1][j] = m[i][-j-1] = 0 if j == 7 else 1\n            elif i in (1, 5):\n                m[i][j] = m[-i-1][j] = m[i][-j-1] = 1 if j in (0, 6) else 0  \n            elif i == 7:\n                m[i][j] = m[-i-1][j] = m[i][-j-1] = 0\n            else:\n                m[i][j] = m[-i-1][j] = m[i][-j-1] = 0 if j in (1, 5, 7) else 1\n    \ndef add_alignment(ver, m):\n    if ver > 1:\n        coordinates = alig_location[ver-2]\n        for i in coordinates:\n            for j in coordinates:\n                if m[i][j] is None:\n                    add_an_alignment(i, j, m)\n            \ndef add_an_alignment(row, column, m):\n    for i in range(row-2, row+3):\n        for j in range(column-2, column+3):\n            m[i][j] = 1 if i in (row-2, row+2) or j in (column-2, column+2) else 0\n    m[row][column] = 1\n    \ndef add_timing(m):\n    for i in range(8, len(m)-8):\n        m[i][6] = m[6][i] = 1 if i % 2 ==0 else 0\n    \ndef add_dark_and_reserving(ver, m):\n    for j in range(8):\n        m[8][j] = m[8][-j-1] = m[j][8] = m[-j-1][8] = 0\n    m[8][8] = 0\n    m[8][6] = m[6][8] = m[-8][8] = 1\n    \n    if ver > 6:\n        for i in range(6):\n            for j in (-9, -10, -11):\n                m[i][j] = m[j][i] = 0\n                \ndef place_bits(bits, m):\n    bit = (int(i) for i in bits)\n\n    up = True\n    for a in range(len(m)-1, 0, -2):\n        a = a-1 if a <= 6 else a\n        irange = range(len(m)-1, -1, -1) if up else range(len(m))\n        for i in irange:\n            for j in (a, a-1):\n                if m[i][j] is None:\n                    m[i][j] = next(bit)\n        up = not up\n  \ndef mask(mm, m):\n    mps = get_mask_patterns(mm)\n    scores = []\n    for mp in mps:\n        for i in range(len(mp)):\n            for j in range(len(mp)):\n                mp[i][j] = mp[i][j] ^ m[i][j]\n        scores.append(compute_score(mp))\n    best = scores.index(min(scores))\n    return best, mps[best]\n    \ndef get_mask_patterns(mm):\n    def formula(i, row, column):\n        if i == 0:\n            return (row + column) % 2 == 0\n        elif i == 1:\n            return row % 2 == 0\n        elif i == 2:\n            return column % 3 == 0\n        elif i == 3:\n            return (row + column) % 3 == 0\n        elif i == 4:\n            return (row // 2 + column // 3) % 2 == 0\n        elif i == 5:\n            return ((row * column) % 2) + ((row * column) % 3) == 0\n        elif i == 6:\n            return (((row * column) % 2) + ((row * column) % 3)) % 2 == 0\n        elif i == 7:\n            return \t(((row + column) % 2) + ((row * column) % 3)) % 2 == 0\n\n    mm[-8][8] = None\n    for i in range(len(mm)):\n        for j in range(len(mm)):\n            mm[i][j] = 0 if mm[i][j] is not None else mm[i][j]\n    mps = []\n    for i in range(8):\n        mp = [ii[:] for ii in mm]\n        for row in range(len(mp)):\n            for column in range(len(mp)):\n                mp[row][column] = 1 if mp[row][column] is None and formula(i, row, column) else 0\n        mps.append(mp)\n        \n    return mps\n            \ndef compute_score(m):\n    def evaluation1(m):\n        def ev1(ma):\n            sc = 0\n            for mi in ma:\n                j = 0\n                while j < len(mi)-4:\n                    n = 4\n                    while mi[j:j+n+1] in [[1]*(n+1), [0]*(n+1)]:\n                        n += 1\n                    (sc, j) = (sc+n-2, j+n) if n > 4 else (sc, j+1)\n            return sc\n        return ev1(m) + ev1(list(map(list, zip(*m))))\n        \n    def evaluation2(m):\n        sc = 0\n        for i in range(len(m)-1):\n            for j in range(len(m)-1):\n                sc += 3 if m[i][j] == m[i+1][j] == m[i][j+1] == m[i+1][j+1] else 0\n        return sc\n        \n    def evaluation3(m):\n        def ev3(ma):\n            sc = 0\n            for mi in ma:\n                j = 0\n                while j < len(mi)-10:\n                    if mi[j:j+11] == [1,0,1,1,1,0,1,0,0,0,0]:\n                        sc += 40\n                        j += 7\n                    elif mi[j:j+11] == [0,0,0,0,1,0,1,1,1,0,1]:\n                        sc += 40\n                        j += 4\n                    else:\n                        j += 1\n            return sc\n        return ev3(m) + ev3(list(map(list, zip(*m))))\n        \n    def evaluation4(m):\n        darknum = 0\n        for i in m:\n            darknum += sum(i)\n        percent = darknum  / (len(m)**2) * 100\n        s = int((50 - percent) / 5) * 5\n        return 2*s if s >=0 else -2*s\n\n    score = evaluation1(m) + evaluation2(m)+ evaluation3(m) + evaluation4(m)\n    return score\n    \ndef add_format_and_version_string(ver, ecl, mask_num, m):\n    fs = [int(i) for i in format_info_str[lindex[ecl]][mask_num]]\n    for j in range(6):\n        m[8][j] = m[-j-1][8] = fs[j]\n        m[8][-j-1] = m[j][8] = fs[-j-1]\n    m[8][7] = m[-7][8] = fs[6]\n    m[8][8] = m[8][-8] = fs[7]\n    m[7][8] = m[8][-7] = fs[8]\n    \n    if ver > 6:\n        vs = (int(i) for i in version_info_str[ver-7])\n        for j in range(5, -1, -1):\n            for i in (-9, -10, -11):\n                m[i][j] = m[j][i] = next(vs)"
  },
  {
    "path": "video_downloader/MyQR/mylibs/structure.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom MyQR.mylibs.constant import required_remainder_bits, lindex, grouping_list\n\ndef structure_final_bits(ver, ecl, data_codewords, ecc):\n    final_message = interleave_dc(ver, ecl, data_codewords) + interleave_ecc(ecc)\n    \n    # convert to binary & Add Remainder Bits if Necessary\n    final_bits = ''.join(['0'*(8-len(i))+i for i in [bin(i)[2:] for i in final_message]]) + '0' * required_remainder_bits[ver-1]\n    \n    return final_bits\n\ndef interleave_dc(ver, ecl, data_codewords):\n    id = []\n    for t in zip(*data_codewords):\n        id += list(t)\n    g = grouping_list[ver-1][lindex[ecl]]\n    if g[3]:\n        for i in range(g[2]):\n            id.append(data_codewords[i-g[2]][-1])\n    return id\n    \ndef interleave_ecc(ecc):\n    ie = []\n    for t in zip(*ecc):\n        ie += list(t)\n    return ie"
  },
  {
    "path": "video_downloader/MyQR/mylibs/theqrmodule.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom MyQR.mylibs import data, ECC, structure, matrix, draw\n\n# ver: Version from 1 to 40\n# ecl: Error Correction Level (L,M,Q,H)\n# get a qrcode picture of 3*3 pixels per module\ndef get_qrcode(ver, ecl, str, save_place):\n    # Data Coding\n    ver, data_codewords = data.encode(ver, ecl, str)\n\n    # Error Correction Coding\n    ecc = ECC.encode(ver, ecl, data_codewords)\n    \n    # Structure final bits\n    final_bits = structure.structure_final_bits(ver, ecl, data_codewords, ecc)\n    \n    # Get the QR Matrix\n    qrmatrix = matrix.get_qrmatrix(ver, ecl, final_bits)\n        \n    # Draw the picture and Save it, then return the real ver and the absolute name\n    return ver, draw.draw_qrcode(save_place, qrmatrix)\n"
  },
  {
    "path": "video_downloader/MyQR/myqr.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport os\nfrom MyQR.mylibs import theqrmodule\nfrom PIL import Image\n   \n# Positional parameters\n#   words: str\n#\n# Optional parameters\n#   version: int, from 1 to 40\n#   level: str, just one of ('L','M','Q','H')\n#   picutre: str, a filename of a image\n#   colorized: bool\n#   constrast: float\n#   brightness: float\n#   save_name: str, the output filename like 'example.png'\n#   save_dir: str, the output directory\n#\n# See [https://github.com/sylnsfar/qrcode] for more details!\ndef run(words, version=1, level='H', picture=None, colorized=False, contrast=1.0, brightness=1.0, save_name=None, save_dir=os.getcwd()):\n\n    supported_chars = r\"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ··,.:;+-*/\\~!@#$%^&`'=<>[]()?_{}|\"\n\n\n    # check every parameter\n    if not isinstance(words, str) or any(i not in supported_chars for i in words):\n        raise ValueError('Wrong words! Make sure the characters are supported!')\n    if not isinstance(version, int) or version not in range(1, 41):\n        raise ValueError('Wrong version! Please choose a int-type value from 1 to 40!')\n    if not isinstance(level, str) or len(level)>1 or level not in 'LMQH':\n        raise ValueError(\"Wrong level! Please choose a str-type level from {'L','M','Q','H'}!\")\n    if picture:\n        if not isinstance(picture, str) or not os.path.isfile(picture) or picture[-4:] not in ('.jpg','.png','.bmp','.gif'):\n            raise ValueError(\"Wrong picture! Input a filename that exists and be tailed with one of {'.jpg', '.png', '.bmp', '.gif'}!\")\n        if picture[-4:] == '.gif' and save_name and save_name[-4:] != '.gif':\n            raise ValueError('Wrong save_name! If the picuter is .gif format, the output filename should be .gif format, too!')\n        if not isinstance(colorized, bool):\n            raise ValueError('Wrong colorized! Input a bool-type value!')\n        if not isinstance(contrast, float):\n            raise ValueError('Wrong contrast! Input a float-type value!')\n        if not isinstance(brightness, float):\n            raise ValueError('Wrong brightness! Input a float-type value!')\n    if save_name and (not isinstance(save_name, str) or save_name[-4:] not in ('.jpg','.png','.bmp','.gif')):\n        raise ValueError(\"Wrong save_name! Input a filename tailed with one of {'.jpg', '.png', '.bmp', '.gif'}!\")\n    if not os.path.isdir(save_dir):\n        raise ValueError('Wrong save_dir! Input a existing-directory!')\n    \n        \n    def combine(ver, qr_name, bg_name, colorized, contrast, brightness, save_dir, save_name=None):\n        from MyQR.mylibs.constant import alig_location\n        from PIL import ImageEnhance, ImageFilter\n        \n        qr = Image.open(qr_name)\n        qr = qr.convert('RGBA') if colorized else qr\n        \n        bg0 = Image.open(bg_name).convert('RGBA')\n        bg0 = ImageEnhance.Contrast(bg0).enhance(contrast)\n        bg0 = ImageEnhance.Brightness(bg0).enhance(brightness)\n\n        if bg0.size[0] < bg0.size[1]:\n            bg0 = bg0.resize((qr.size[0]-24, (qr.size[0]-24)*int(bg0.size[1]/bg0.size[0])))\n        else:\n            bg0 = bg0.resize(((qr.size[1]-24)*int(bg0.size[0]/bg0.size[1]), qr.size[1]-24))    \n            \n        bg = bg0 if colorized else bg0.convert('1')\n        \n        aligs = []\n        if ver > 1:\n            aloc = alig_location[ver-2]\n            for a in range(len(aloc)):\n                for b in range(len(aloc)):\n                    if not ((a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)):\n                        for i in range(3*(aloc[a]-2), 3*(aloc[a]+3)):\n                            for j in range(3*(aloc[b]-2), 3*(aloc[b]+3)):\n                                aligs.append((i,j))\n\n        for i in range(qr.size[0]-24):\n            for j in range(qr.size[1]-24):\n                if not ((i in (18,19,20)) or (j in (18,19,20)) or (i<24 and j<24) or (i<24 and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24) or ((i,j) in aligs) or (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)):\n                    qr.putpixel((i+12,j+12), bg.getpixel((i,j)))\n        \n        qr_name = os.path.join(save_dir, os.path.splitext(os.path.basename(bg_name))[0] + '_qrcode.png') if not save_name else os.path.join(save_dir, save_name)\n        qr.resize((qr.size[0]*3, qr.size[1]*3)).save(qr_name)\n        return qr_name\n\n    tempdir = os.path.join(os.path.expanduser('~'), '.myqr')\n    \n    try:\n        if not os.path.exists(tempdir):\n            os.makedirs(tempdir)\n\n        ver, qr_name = theqrmodule.get_qrcode(version, level, words, tempdir)\n\n        if picture and picture[-4:]=='.gif':\n            import imageio\n             \n            im = Image.open(picture)\n            duration = im.info.get('duration', 0)\n            im.save(os.path.join(tempdir, '0.png'))\n            while True:\n                try:\n                    seq = im.tell()\n                    im.seek(seq + 1)\n                    im.save(os.path.join(tempdir, '%s.png' %(seq+1)))\n                except EOFError:\n                    break\n            \n            imsname = []\n            for s in range(seq+1):\n                bg_name = os.path.join(tempdir, '%s.png' % s)\n                imsname.append(combine(ver, qr_name, bg_name, colorized, contrast, brightness, tempdir))\n            \n            ims = [imageio.imread(pic) for pic in imsname]\n            qr_name = os.path.join(save_dir, os.path.splitext(os.path.basename(picture))[0] + '_qrcode.gif') if not save_name else os.path.join(save_dir, save_name)\n            imageio.mimwrite(qr_name, ims, '.gif', **{ 'duration': duration/1000 })\n        elif picture:\n            qr_name = combine(ver, qr_name, picture, colorized, contrast, brightness, save_dir, save_name)\n        elif qr_name:\n            qr = Image.open(qr_name)\n            qr_name = os.path.join(save_dir, os.path.basename(qr_name)) if not save_name else os.path.join(save_dir, save_name)\n            qr.resize((qr.size[0]*3, qr.size[1]*3)).save(qr_name)\n          \n        return ver, level, qr_name\n        \n    except:\n        raise\n    finally:\n        import shutil\n        if os.path.exists(tempdir):\n            shutil.rmtree(tempdir) "
  },
  {
    "path": "video_downloader/MyQR/terminal.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom MyQR.myqr import run\nimport os\n\ndef main():\n    import argparse\n    argparser = argparse.ArgumentParser()\n    argparser.add_argument('Words', help = 'The words to produce you QR-code picture, like a URL or a sentence. Please read the README file for the supported characters.')\n    argparser.add_argument('-v', '--version', type = int, choices = range(1,41), default = 1, help = 'The version means the length of a side of the QR-Code picture. From little size to large is 1 to 40.')\n    argparser.add_argument('-l', '--level', choices = list('LMQH'), default = 'H', help = 'Use this argument to choose an Error-Correction-Level: L(Low), M(Medium) or Q(Quartile), H(High). Otherwise, just use the default one: H')\n    argparser.add_argument('-p', '--picture', help = 'the picture  e.g. example.jpg')\n    argparser.add_argument('-c', '--colorized', action = 'store_true', help = \"Produce a colorized QR-Code with your picture. Just works when there is a correct '-p' or '--picture'.\")\n    argparser.add_argument('-con', '--contrast', type = float, default = 1.0, help = 'A floating point value controlling the enhancement of contrast. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value. Default: 1.0')\n    argparser.add_argument('-bri', '--brightness', type = float, default = 1.0, help = 'A floating point value controlling the enhancement of brightness. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value. Default: 1.0')\n    argparser.add_argument('-n', '--name', help = \"The filename of output tailed with one of {'.jpg', '.png', '.bmp', '.gif'}. eg. exampl.png\")\n    argparser.add_argument('-d', '--directory', default = os.getcwd(), help = 'The directory of output.')\n    args = argparser.parse_args()\n    \n    if args.picture and args.picture[-4:]=='.gif':\n        print('It may take a while, please wait for minutes...')\n    \n    try:\n        ver, ecl, qr_name = run(\n            args.Words,\n            args.version,\n            args.level,\n            args.picture,\n            args.colorized,\n            args.contrast,\n            args.brightness,\n            args.name,\n            args.directory\n            )   \n        print('Succeed! \\nCheck out your', str(ver) + '-' + str(ecl), 'QR-code:', qr_name)\n    except:\n        raise"
  },
  {
    "path": "video_downloader/requirements.txt",
    "content": "imageio\nnumpy\nPillow\nbeautifulsoup4\n"
  },
  {
    "path": "video_downloader/video_downloader.py",
    "content": "# -*- coding:utf-8 -*-\nfrom tkinter.filedialog import askdirectory\nfrom MyQR.myqr import run\nfrom urllib import request, parse\nfrom bs4 import BeautifulSoup\n\nimport tkinter.messagebox as msgbox\nimport tkinter as tk\nimport webbrowser\nimport re\nimport json\nimport os\nimport types\nimport requests\nimport time\n\n\n\"\"\"\n类说明:爱奇艺、优酷等实现在线观看以及视频下载的类\n\nParameters:\n\twidth - tkinter主界面宽\n\theight - tkinter主界面高\n\nReturns:\n\t无\n\nModify:\n\t2017-05-09\n\"\"\"\nclass APP:\n\tdef __init__(self, width = 500, height = 300):\n\t\tself.w = width\n\t\tself.h = height\n\t\tself.title = ' VIP视频破解助手'\n\t\tself.root = tk.Tk(className=self.title)\n\t\tself.url = tk.StringVar()\n\t\tself.v = tk.IntVar()\n\t\tself.v.set(1)\n\n\n\t\t#Frame空间\n\t\tframe_1 = tk.Frame(self.root)\n\t\tframe_2 = tk.Frame(self.root)\n\t\tframe_3 = tk.Frame(self.root)\n\t\t\n\t\t#Menu菜单\n\t\tmenu = tk.Menu(self.root)\n\t\tself.root.config(menu = menu)\n\t\tfilemenu = tk.Menu(menu,tearoff=0)\n\t\tmoviemenu = tk.Menu(menu,tearoff = 0)\n\t\tmenu.add_cascade(label = '菜单', menu = filemenu)\n\t\tmenu.add_cascade(label = '友情链接', menu = moviemenu)\n\t\tfilemenu.add_command(label = '使用说明',command = lambda :webbrowser.open('http://blog.csdn.net/c406495762/article/details/71334633'))\n\t\tfilemenu.add_command(label = '关于作者',command = lambda :webbrowser.open('http://blog.csdn.net/c406495762'))\n\t\tfilemenu.add_command(label = '退出',command = self.root.quit)\n\n\t\t#各个网站链接\n\t\tmoviemenu.add_command(label = '网易公开课',command = lambda :webbrowser.open('http://open.163.com/'))\n\t\tmoviemenu.add_command(label = '腾讯视频',command = lambda :webbrowser.open('http://v.qq.com/'))\n\t\tmoviemenu.add_command(label = '搜狐视频',command = lambda :webbrowser.open('http://tv.sohu.com/'))\n\t\tmoviemenu.add_command(label = '芒果TV',command = lambda :webbrowser.open('http://www.mgtv.com/'))\n\t\tmoviemenu.add_command(label = '爱奇艺',command = lambda :webbrowser.open('http://www.iqiyi.com/'))\n\t\tmoviemenu.add_command(label = 'PPTV',command = lambda :webbrowser.open('http://www.bilibili.com/'))\n\t\tmoviemenu.add_command(label = '优酷',command = lambda :webbrowser.open('http://www.youku.com/'))\n\t\tmoviemenu.add_command(label = '乐视',command = lambda :webbrowser.open('http://www.le.com/'))\n\t\tmoviemenu.add_command(label = '土豆',command = lambda :webbrowser.open('http://www.tudou.com/'))\n\t\tmoviemenu.add_command(label = 'A站',command = lambda :webbrowser.open('http://www.acfun.tv/'))\n\t\tmoviemenu.add_command(label = 'B站',command = lambda :webbrowser.open('http://www.bilibili.com/'))\n\n\t\t#控件内容设置\n\t\tgroup = tk.Label(frame_1,text = '请选择一个视频播放通道：', padx = 10, pady = 10)\n\t\ttb1 = tk.Radiobutton(frame_1,text = '通道一', variable = self.v, value = 1, width = 10, height = 3)\n\t\ttb2 = tk.Radiobutton(frame_1,text = '通道二', variable = self.v, value = 2, width = 10, height = 3)\n\t\tlabel1 = tk.Label(frame_2, text = \"请输入视频链接：\")\n\t\tentry = tk.Entry(frame_2, textvariable = self.url, highlightcolor = 'Fuchsia', highlightthickness = 1,width = 35)\n\t\tlabel2 = tk.Label(frame_2, text = \" \")\n\t\tplay = tk.Button(frame_2, text = \"播放\", font = ('楷体',12), fg = 'Purple', width = 2, height = 1, command = self.video_play)\n\t\tlabel3 = tk.Label(frame_2, text = \" \")\n\t\t# download = tk.Button(frame_2, text = \"下载\", font = ('楷体',12), fg = 'Purple', width = 2, height = 1, command = self.download_wmxz)\n\t\tQR_Code = tk.Button(frame_3, text = \"手机观看\", font = ('楷体',12), fg = 'Purple', width = 10, height = 2, command = self.QR_Code)\n\t\tlabel_explain = tk.Label(frame_3, fg = 'red', font = ('楷体',12), text = '\\n注意：支持大部分主流视频网站的视频播放！\\n此软件仅用于交流学习，请勿用于任何商业用途！')\n\t\tlabel_warning = tk.Label(frame_3, fg = 'blue', font = ('楷体',12),text = '\\n建议：将Chrome内核浏览器设置为默认浏览器\\n作者:Jack_Cui')\n\n\n\n\t\t#控件布局\n\t\tframe_1.pack()\n\t\tframe_2.pack()\n\t\tframe_3.pack()\n\t\tgroup.grid(row = 0, column = 0)\n\t\ttb1.grid(row = 0, column = 1)\n\t\ttb2.grid(row = 0, column = 2)\n\t\tlabel1.grid(row = 0, column = 0)\n\t\tentry.grid(row = 0, column = 1)\n\t\tlabel2.grid(row = 0, column = 2)\n\t\tplay.grid(row = 0, column = 3,ipadx = 10, ipady = 10)\n\t\tlabel3.grid(row = 0, column = 4)\n\t\t# download.grid(row = 0, column = 5,ipadx = 10, ipady = 10)\n\t\tQR_Code.grid(row = 0, column = 0)\n\t\tlabel_explain.grid(row = 1, column = 0)\n\t\tlabel_warning.grid(row = 2, column = 0)\n\n\t\"\"\"\n\t函数说明:jsonp解析\n\n\tParameters:\n\t\t_jsonp - jsonp字符串\n\n\tReturns:\n\t\t_json - json格式数据\n\n\tModify:\n\t\t2017-05-11\n\t\"\"\"\n\tdef loads_jsonp(self, _jsonp):\n\t\ttry:\n\t\t\t_json = json.loads(re.match(\".*?({.*}).*\",_jsonp,re.S).group(1))\n\t\t\treturn _json\n\t\texcept:\n\t\t\traise ValueError('Invalid Input')\n\n\t\"\"\"\n\t函数说明:视频播放\n\n\tParameters:\n\t\tself\n\n\tReturns:\n\t\t无\n\n\tModify:\n\t\t2017-05-09\n\t\"\"\"\n\tdef video_play(self):\n\t\t#视频解析网站地址\n\t\tport_1 = 'http://www.wmxz.wang/video.php?url='\n\t\tport_2 = 'http://www.vipjiexi.com/tong.php?url='\n\n\t\t#正则表达是判定是否为合法链接\n\t\tif re.match(r'^https?:/{2}\\w.+$', self.url.get()):\n\t\t\tif self.v.get() == 1:\n\t\t\t\t#视频链接获取\n\t\t\t\tip = self.url.get()\n\t\t\t\t#视频链接加密\n\t\t\t\tip = parse.quote_plus(ip)\n\t\t\t\t#浏览器打开\n\t\t\t\twebbrowser.open(port_1 + self.url.get())\n\t\t\telif self.v.get() == 2:\n\t\t\t\t#链接获取\n\t\t\t\tip = self.url.get()\n\t\t\t\t#链接加密\n\t\t\t\tip = parse.quote_plus(ip)\n\n\t\t\t\t#获取time、key、url\n\t\t\t\tget_url = 'http://www.vipjiexi.com/x2/tong.php?url=%s' % ip \n\t\t\t\t# get_url_head = {\n\t\t\t\t# \t'User-Agent':'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19',\n\t\t\t\t# \t'Referer':'http://www.vipjiexi.com/',\n\t\t\t\t# }\n\t\t\t\t# get_url_req = request.Request(url = get_url, headers = get_url_head)\n\t\t\t\t# get_url_response = request.urlopen(get_url_req)\n\t\t\t\t# get_url_html = get_url_response.read().decode('utf-8')\n\t\t\t\t# bf = BeautifulSoup(get_url_html, 'lxml')\n\t\t\t\t# a = str(bf.find_all('script'))\n\t\t\t\t# pattern = re.compile('\"api.php\", {\"time\":\"(\\d+)\", \"key\": \"(.+)\", \"url\": \"(.+)\",\"type\"', re.IGNORECASE)\n\t\t\t\t# string = pattern.findall(a)\n\t\t\t\t# now_time = string[0][0]\n\t\t\t\t# now_key = string[0][1]\n\t\t\t\t# now_url = string[0][2] \n\n\t\t\t\t# #请求播放,获取Success = 1\n\t\t\t\t# get_movie_url = 'http://www.vipjiexi.com/x2/api.php'\n\t\t\t\t# get_movie_data = {\n\t\t\t\t# \t'key':'%s' % now_key,\n\t\t\t\t# \t'time':'%s' % now_time,\n\t\t\t\t# \t'type':'',\n\t\t\t\t# \t'url':'%s' % now_url\n\t\t\t\t# }\n\t\t\t\t# get_movie_head = {\n\t\t\t\t# \t'User-Agent':'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19',\n\t\t\t\t# \t'Referer':'http://www.vipjiexi.com/x2/tong.php?',\n\t\t\t\t# \t'url':'%s' % ip,\n\t\t\t\t# }\n\t\t\t\t# get_movie_req = request.Request(url = get_movie_url, headers = get_movie_head)\n\t\t\t\t# get_movie_data = parse.urlencode(get_movie_data).encode('utf-8')\n\t\t\t\t# get_movie_response = request.urlopen(get_movie_req, get_movie_data)\n\t\t\t\t#请求之后立刻打开\n\t\t\t\twebbrowser.open(get_url)\n\n\t\telse:\n\t\t\tmsgbox.showerror(title='错误',message='视频链接地址无效，请重新输入！')\n\n\t\"\"\"\n\t函数说明:视频下载，通过无名小站抓包(已经无法使用)\n\n\tParameters:\n\t\tself\n\n\tReturns:\n\t\t无\n\n\tModify:\n\t\t2017-06-15\n\t\"\"\"\n\tdef download_wmxz(self):\t\n\t\tif re.match(r'^https?:/{2}\\w.+$', self.url.get()):\n\t\t\t#视频链接获取\n\t\t\tip = self.url.get()\n\t\t\t#视频链接加密\n\t\t\tip = parse.quote_plus(ip)\n\n\t\t\t#获取保存视频的url\n\t\t\tget_url = 'http://www.sfsft.com/index.php?url=%s' % ip \n\t\t\thead = {\n\t\t\t\t'User-Agent':'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19',\n\t\t\t\t'Referer':'http://www.sfsft.com/index.php?url=%s' % ip\n\t\t\t}\n\t\t\tget_url_req = request.Request(url = get_url, headers = head)\n\t\t\tget_url_response = request.urlopen(get_url_req)\n\t\t\tget_url_html = get_url_response.read().decode('utf-8')\n\t\t\tbf = BeautifulSoup(get_url_html, 'lxml')\n\t\t\ta = str(bf.find_all('script'))\n\t\t\tpattern = re.compile(\"url : '(.+)',\", re.IGNORECASE)\n\t\t\turl = pattern.findall(a)[0]\n\n\t\t\t#获取视频地址\n\t\t\tget_movie_url = 'http://www.sfsft.com/api.php'\n\t\t\tget_movie_data = {\n\t\t\t\t'up':'0',\n\t\t\t\t'url':'%s' % url,\n\t\t\t}\n\t\t\tget_movie_req = request.Request(url = get_movie_url, headers = head)\n\t\t\tget_movie_data = parse.urlencode(get_movie_data).encode('utf-8')\n\t\t\tget_movie_response = request.urlopen(get_movie_req, get_movie_data)\n\t\t\tget_movie_html = get_movie_response.read().decode('utf-8')\n\t\t\tget_movie_data = json.loads(get_movie_html)\n\t\t\twebbrowser.open(get_movie_data['url'])\n\t\telse:\n\t\t\tmsgbox.showerror(title='错误',message='视频链接地址无效，请重新输入！')\n\n\n\t\"\"\"\n\t函数说明:生成二维码,手机观看\n\n\tParameters:\n\t\tself\n\n\tReturns:\n\t\t无\n\n\tModify:\n\t\t2017-05-12\n\t\"\"\"\n\tdef QR_Code(self):\t\n\t\tif re.match(r'^https?:/{2}\\w.+$', self.url.get()):\n\t\t\t#视频链接获取\n\t\t\tip = self.url.get()\n\t\t\t#视频链接加密\n\t\t\tip = parse.quote_plus(ip)\n\n\t\t\turl = 'http://www.wmxz.wang/video.php?url=%s' % ip\n\t\t\twords = url\n\t\t\timages_pwd = os.getcwd() + '\\Images\\\\'\n\t\t\tpng_path = images_pwd + 'bg.png'\n\t\t\tqr_name = 'qrcode.png'\n\t\t\tqr_path = images_pwd + 'qrcode.png'\n\n\t\t\trun(words = words, picture = png_path, save_name = qr_name, save_dir = images_pwd)\n\n\t\t\ttop = tk.Toplevel(self.root)\n\t\t\timg = tk.PhotoImage(file = qr_path)\n\t\t\ttext_label = tk.Label(top, fg = 'red', font = ('楷体',15), text = \"手机浏览器扫描二维码，在线观看视频！\")\n\t\t\timg_label = tk.Label(top, image = img)\n\t\t\ttext_label.pack()\n\t\t\timg_label.pack()\n\t\t\ttop.mainloop()\n\n\t\telse:\n\t\t\tmsgbox.showerror(title='错误',message='视频链接地址无效，请重新输入！')\n\n\t\"\"\"\n\t函数说明:tkinter窗口居中\n\n\tParameters:\n\t\tself\n\n\tReturns:\n\t\t无\n\n\tModify:\n\t\t2017-05-09\n\t\"\"\"\n\tdef center(self):\n\t\tws = self.root.winfo_screenwidth()\n\t\ths = self.root.winfo_screenheight()\n\t\tx = int( (ws/2) - (self.w/2) )\n\t\ty = int( (hs/2) - (self.h/2) )\n\t\tself.root.geometry('{}x{}+{}+{}'.format(self.w, self.h, x, y))\n\n\t\"\"\"\n\t函数说明:loop等待用户事件\n\n\tParameters:\n\t\tself\n\n\tReturns:\n\t\t无\n\n\tModify:\n\t\t2017-05-09\n\t\"\"\"\n\tdef loop(self):\n\t\tself.root.resizable(False, False)\t#禁止修改窗口大小\n\t\tself.center()\t\t\t\t\t\t#窗口居中\n\t\tself.root.mainloop()\n\nif __name__ == '__main__':\n\tapp = APP()\t\t\t#实例化APP对象\n\tapp.loop()\t\t\t#loop等待用户事件\n\n\n\n\n"
  },
  {
    "path": "zhengfang_system_spider/README.md",
    "content": "# ZhengFang_System_Spider\n对正方教务管理系统的个人课表，个人学生成绩，绩点等简单爬取\n\n## 依赖环境\npython 3.6\n### python库\nhttp请求：requests，urllib  \n数据提取：re，lxml，bs4  \n存储相关：os，sys  \n验证码处理：PIL  \n\n## 下载安装\n在终端输入如下命令：\n```bash\ngit clone git@github.com:Jack-Cherish/python-spider.git\n```\n\n## 使用方法\n\n### 安装依赖包\n```bash\npip install -r requirements.txt\n```\n\n### 运行\n在当前目录下输入：\n```\ncd zhengfang_system_spider\npython spider.py\n```\n运行爬虫，按提示输入学校教务网，学号，密码，输入验证码  \n\n![运行时](/zhengfang_system_spider/screenshot/spider.png)\n\n稍等几秒钟，当前ZhengFang_System_Spider文件夹下就会生成zhengfang.txt  \n个人课表，成绩绩点均已保存到该文本文件中\n\n![结果](/zhengfang_system_spider/screenshot/zf.png)\n"
  },
  {
    "path": "zhengfang_system_spider/requirements.txt",
    "content": "lxml==4.6.3\nrequests==2.20.0\nPillow>=6.2.2\nbeautifulsoup4==4.6.0\n"
  },
  {
    "path": "zhengfang_system_spider/spider.py",
    "content": "#!/usr/bin/env python\n#-*- coding: utf-8 -*-\n\n__author__ = 'ZYSzys'\n\nimport requests\nimport re\nimport os\nimport sys\nimport urllib\nimport getpass\nfrom lxml import etree\nfrom PIL import Image\nfrom imp import reload\nfrom bs4 import BeautifulSoup\n\n\nclass Who:\n    def __init__(self, user, pswd):\n        self.user = user\n        self.pswd = pswd\n\n\nclass Tool:\n    rma = re.compile('<a href=.*?>|</a>')\n    rmtb = re.compile('<br />|</br>|<br>')\n    rmtr = re.compile('<td>|</td>|<tr>|</tr>|<tr class=\"alt\">|<tr class=\"datelisthead\">')\n    rmtime1 = re.compile('<td align=\"Center\" width=\"7%\">.*?</td>')\n    rmtime2 = re.compile('<td class=\"noprint\" align=\"Center\".*?>.*?</td>')\n\n    def replace(self, x):\n        x = re.sub(self.rma, '   ', x)\n        x = re.sub(self.rmtb, '---', x)\n        x = re.sub(self.rmtr, '  ', x)\n        x = re.sub(self.rmtime1, '\\n', x)\n        x = re.sub(self.rmtime2, '', x)\n        return x.strip()\n\n\ndef Getgrade(response):\n    html = response.content\n    soup = BeautifulSoup(html, 'lxml')\n    trs = soup.find(id=\"Datagrid1\").findAll(\"tr\")\n    Grades = []\n    keys = []\n    tds = trs[0].findAll(\"td\")\n    tds = tds[:2] + tds[3:5] + tds[6:9]\n    for td in tds:\n        keys.append(td.string)\n    for tr in trs[1:]:\n        tds = tr.findAll(\"td\")\n        tds = tds[:2] + tds[3:5] + tds[6:9]\n        values = []\n        for td in tds:\n            values.append(td.string)\n        one = dict((key, value) for key, value in zip(keys, values))\n        Grades.append(one)\n    return Grades\n\n\ndef Getgradetestresults(trs):\n    results = []\n    k = []\n    for td in trs[0].xpath('.//td/text()'):\n        k.append(td)\n    trs = trs[1:]\n    for tr in trs:\n        tds = tr.xpath('.//td/text()')\n        v = []\n        for td in tds:\n            v.append(td)\n        one = dict((i, j) for i, j in zip(k, v))\n        results.append(one)\n    return results\n\n\nclass University:\n    def __init__(self, student, baseurl):\n        reload(sys)\n        self.student = student\n        self.baseurl = baseurl\n        self.session = requests.session()\n        self.session.headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'\n\n    def Login(self):\n        url = self.baseurl+'/default2.aspx'\n        res = self.session.get(url)\n        cont = res.content\n        selector = etree.HTML(cont)\n        __VIEWSTATE = selector.xpath('//*[@id=\"form1\"]/input/@value')[0]\n        imgurl = self.baseurl + '/CheckCode.aspx'\n        imgres = self.session.get(imgurl, stream=True)\n        img = imgres.content\n        with open('code.jpg', 'wb') as f:\n            f.write(img)\n        jpg = Image.open('{}/code.jpg'.format(os.getcwd()))\n        jpg.show()\n        jpg.close\n        code = input('输入验证码：')\n        RadioButtonList1 = u\"学生\"\n        data = {\n            \"__VIEWSTATE\": __VIEWSTATE,\n            \"txtUserName\": self.student.user,\n            \"TextBox1\": self.student.pswd,\n            \"TextBox2\": self.student.pswd,\n            \"txtSecretCode\": code,\n            \"RadioButtonList1\": RadioButtonList1,\n            \"Button1\": \"\",\n            \"lbLanguage\": \"\"\n        }\n        loginres = self.session.post(url, data=data)\n        logcont = loginres.text\n        pattern = re.compile(\n            '<form name=\"Form1\".*?action=(.*?) id=\"Form1\">', re.S)\n        res = re.findall(pattern, logcont)\n        try:\n            if res[0][17:29] == self.student.user:\n                print('Login succeed!')\n        except:\n            print('Login failed! Maybe Wrong password ! ! !')\n            return\n        pattern = re.compile('<span id=\"xhxm\">(.*?)</span>')\n        xhxm = re.findall(pattern, logcont)\n        name = xhxm[0].replace('同学', '')\n        self.student.urlname = urllib.parse.quote_plus(str(name))\n        return True\n\n    def GetClass(self):\n        self.session.headers['Referer'] = self.baseurl + \\\n            '/xs_main.aspx?xh=' + self.student.user\n        kburl = self.baseurl + '/xskbcx.aspx?xh='+self.student.user + \\\n            '&xm='+self.student.urlname+'&gnmkdm=N121603'\n        kbresponse = self.session.get(kburl)\n        kbcont = kbresponse.text\n        pattern = re.compile('<td.*?align=\"Center\".*?>(.*?)</td>', re.S)\n        contents = re.findall(pattern, kbcont)\n        tool = Tool()\n        f = open(os.getcwd()+'/zhengfang.txt', 'w')\n        f.write(u'本学期课表:'+'\\n')\n        cnt = 1\n        l = [u'周一', u'周二', u'周三', u'周四', u'周五', u'周六', u'周日']\n        for day in l:\n            for i in contents:\n                if u'星期' in i:\n                    continue\n                elif u'第' in i:\n                    if day in i:\n                        con = tool.replace(i)\n                        f.write(str(cnt)+':\\t'+con+'\\n')\n                        cnt += 1\n                else:\n                    continue\n            f.write('\\n')\n        f.close()\n        print('Download class succeed!')\n\n    def GetGrade(self):\n        self.session.headers['Referer'] = self.baseurl + \\\n            '/xs_main.aspx?xh=' + self.student.user\n        gradeurl = self.baseurl + '/xscjcx.aspx?xh='+self.student.user + \\\n            '&xm='+self.student.urlname+'&gnmkdm=N121605'\n        graderesponse = self.session.get(gradeurl)\n        gradecont = graderesponse.content\n        soup = BeautifulSoup(gradecont, 'lxml')\n        __VIEWSTATE = soup.findAll(name=\"input\")[2][\"value\"]\n        self.session.headers['Referer'] = gradeurl\n        data = {\n            \"__EVENTTARGET\": \"\",\n            \"__EVENTARGUMENT\": \"\",\n            \"__VIEWSTATE\": __VIEWSTATE,\n            \"hidLanguage\": \"\",\n            \"ddlXN\": \"\",\n            \"ddlXQ\": \"\",\n            \"ddl_kcxz\": \"\",\n            \"btn_zcj\": u'历年成绩'\n        }\n        grares = self.session.post(gradeurl, data=data)\n        grades = Getgrade(grares)\n        totup = 0\n        totdown = 0\n        f = open(os.getcwd()+'/zhengfang.txt', 'a+')\n        f.write('\\n\\n\\n'+u'历年成绩:'+'\\n')\n        for i in grades[0]:\n            f.write('%-13s\\t' % i)\n        f.write('\\n')\n        for each in grades:\n            for one in each:\n                f.write('%-15s\\t' % each[one])\n            f.write('\\n')\n            totup = totup + float(each[u'绩点']) * float(each[u'学分'])\n            totdown = totdown + float(each[u'学分'])\n        f.write('\\n'+u'平均绩点: '+'%.2f\\t\\t\\t' % (totup / totdown) +\n                u'总学分绩点: '+'%.2f\\t\\t\\t' % totup + u'总学分: '+'%.2f\\n' % totdown)\n        f.close()\n        print('Download grade succeed!')\n\n    def GradeTestResults(self):\n        self.session.headers['Referer'] = self.baseurl + \\\n            '/xs_main.aspx?xh=' + self.student.user\n        gtrurl = self.baseurl + '/xsdjkscx.aspx?xh='+self.student.user + \\\n            '&xm='+self.student.urlname+'&gnmkdm=N121606'\n        gtrresponse = self.session.get(gtrurl)\n        gtrcontent = gtrresponse.text\n        gtrhtml = etree.HTML(gtrcontent)\n        trs = gtrhtml.xpath('//table[@class=\"datelist\"]/tr')\n        f = open(os.getcwd()+'/zhengfang.txt', 'a+')\n        f.write('\\n\\n\\n'+u'等级考试成绩:'+'\\n')\n        results = Getgradetestresults(trs)\n        for one in results[0]:\n            f.write('%-10s\\t' % one)\n        f.write('\\n')\n        for each in results:\n            for one in each:\n                f.write('%-10s\\t' % each[one])\n            f.write('\\n')\n        f.close()\n        print('Download grade test results succeed!')\n\n\nif __name__ == \"__main__\":\n    url = input(\"学校教务网站(如http://115.236.84.162)：\")\n    user = input(\"学号：\")\n    pswd = getpass.getpass(\"密码：\")\n    who = Who(user, pswd)\n    univ = University(who, url)\n    if univ.Login():\n        univ.GetClass()\n        univ.GetGrade()\n        univ.GradeTestResults()\n"
  },
  {
    "path": "zhengfang_system_spider/zhengfang.txt",
    "content": "本学期课表:\n1:\t电工电子技术基础AⅡ---周一第1,2节{第2-16周|双周}---郜园园/章云(章云,郜园园)---学10609(实验室)\n2:\t计算机网络A---周一第3,4节{第2-16周|双周}---吴晓平(吴晓平)---学10311(实验室)\n3:\t数据库原理与技术B---周一第6,7节{第1-12周}---刘丽娟(刘丽娟)---学1502(智慧教室)\n4:\t数据库原理与技术B---周一第8节{第1-12周}---刘丽娟(刘丽娟)---学1502(智慧教室)\n\n5:\t数据库原理与技术B---周二第1,2节{第1-16周}---刘丽娟(刘丽娟)---学10309(实验室)\n6:\t计算机网络A---周二第3,4,5节{第1-16周}---吴晓平(吴晓平)---教1512(多媒体)\n7:\tJ2EE程序设计---周二第6,7节{第1-16周}---陈文辉(陈文辉)---教5402(多媒体)\n\n8:\t大学体育（篮球）---周三第3,4节{第1-17周}---田晓鹏---东湖风雨操场\n9:\tJ2EE程序设计---周三第6,7节{第1-16周}---陈文辉(陈文辉)---学10309(实验室)\n10:\t毛泽东思想和中国特色社会主义理论体系概论---周三第8,9节{第2-16周}---张国泉---教1401(多媒体)---2018年06月30日(10:20-11:10)---学10203(实验室)\n\n11:\t中国文化英语---周四第1,2节{第1-16周}---陈献---教5302(多媒体)\n12:\t电工电子技术基础AⅡ---周四第3,4,5节{第1-16周}---郜园园/章云(章云,郜园园)---教5403(多媒体)\n13:\t物联网工程概论A---周四第6,7节{第2-16周|双周}---孔汶汶/张建锋/冯海林/吴剑(孔汶汶,张建锋)---学10603(实验室)\n\n14:\t毛泽东思想和中国特色社会主义理论体系概论---周五第3,4节{第2-16周}---张国泉---教1401(多媒体)---2018年06月30日(10:20-11:10)---学10203(实验室)\n15:\t物联网工程概论A---周五第8,9节{第1-16周}---孔汶汶/张建锋/冯海林/吴剑(孔汶汶,张建锋)---教1512(多媒体)\n\n\n16:\t思辨与创新（网络课程）---周日第12节{第1-15周}---网络教师---------用经济学智慧解读中国（网络课程）---周日第12节{第1-15周}---网络教师---\n\n\n\n\n历年成绩:\n学年           \t学期           \t课程名称         \t课程性质         \t学分           \t绩点           \t成绩           \t\n2016-2017      \t1              \t思想道德修养与法律基础    \t必修             \t3              \t   2.80        \t78             \t\n2016-2017      \t1              \t形势与政策          \t必修             \t0.5            \t   4.50        \t优              \t\n2016-2017      \t1              \t大学生心理健康教育      \t必修             \t1              \t   3.30        \t83             \t\n2016-2017      \t1              \t大学生职业发展        \t必修             \t0.5            \t   3.80        \t88             \t\n2016-2017      \t1              \t高级语言程序设计       \t必修             \t4              \t   4.40        \t94             \t\n2016-2017      \t1              \t学业指导           \t必修             \t0.5            \t   4.40        \t94             \t\n2016-2017      \t1              \t大学计算机基础A       \t必修             \t1              \t   3.90        \t89             \t\n2016-2017      \t1              \t高等数学AI         \t必修             \t4              \t   4.50        \t95             \t\n2016-2017      \t1              \t线性代数A          \t必修             \t3              \t   4.60        \t96             \t\n2016-2017      \t1              \t大学英语BI         \t必修             \t4              \t   3.60        \t86             \t\n2016-2017      \t1              \t军事理论           \t必修             \t0.5            \t   3.50        \t良              \t\n2016-2017      \t1              \t军事技能训练         \t必修             \t0.5            \t   4.50        \t95             \t\n2016-2017      \t1              \t大学体育I          \t必修             \t0.75           \t   2.30        \t73             \t\n2016-2017      \t2              \t马克思主义基本原理概论    \t必修             \t3              \t   2.20        \t72             \t\n2016-2017      \t2              \t形势与政策          \t必修             \t0.5            \t   4.50        \t优              \t\n2016-2017      \t2              \t信息技术导论         \t必修             \t1.5            \t   4.40        \t94             \t\n2016-2017      \t2              \t数据结构C          \t必修             \t3.5            \t   4.10        \t91             \t\n2016-2017      \t2              \t数据结构C实习        \t必修             \t1              \t   4.50        \t优              \t\n2016-2017      \t2              \t应用文写作          \t必修             \t2              \t   3.90        \t89             \t\n2016-2017      \t2              \t高等数学AII        \t必修             \t5              \t   4.20        \t92             \t\n2016-2017      \t2              \t大学物理AI         \t必修             \t3              \t   2.90        \t79             \t\n2016-2017      \t2              \t大学英语BII        \t必修             \t4              \t   3.20        \t82             \t\n2016-2017      \t2              \t大学体育II         \t必修             \t0.75           \t   2.50        \t75             \t\n2017-2018      \t1              \t中国近现代史纲要       \t必修             \t2              \t   2.30        \t73             \t\n2017-2018      \t1              \t形势与政策          \t必修             \t0.5            \t   4.50        \t优              \t\n2017-2018      \t1              \t电工电子技术基础AI     \t必修             \t3.5            \t   2.60        \t76             \t\n2017-2018      \t1              \t概率论与数理统计A      \t必修             \t4              \t   4.70        \t97             \t\n2017-2018      \t1              \t大学物理AII        \t必修             \t3              \t   3.30        \t83             \t\n2017-2018      \t1              \t大学物理A实验        \t必修             \t1.5            \t   3.40        \t84             \t\n2017-2018      \t1              \t英语报刊选读         \t必修             \t2              \t   3.40        \t84             \t\n2017-2018      \t1              \t大学体育III        \t必修             \t0.75           \t   3.00        \t80             \t\n2017-2018      \t1              \t生命科学与生物技术导论B   \t选修             \t2              \t   3.50        \t85             \t\n2017-2018      \t1              \t动物福利B（双语)      \t选修             \t2              \t   4.50        \t95             \t\n2017-2018      \t1              \tJAVA程序设计B      \t选修             \t3              \t   4.50        \t95             \t\n2017-2018      \t1              \t面向对象程序设计B      \t选修             \t3.5            \t   4.10        \t91             \t\n2017-2018      \t1              \t专业认知实习         \t选修             \t0.5            \t   4.70        \t97             \t\n2017-2018      \t1              \t个人理财规划（网络课程）   \t选修             \t2              \t   4.50        \t优              \t\n2017-2018      \t1              \t淘宝店铺设计与制作(实验室开放项目)\t选修             \t1              \t   3.20        \t82             \t\n2017-2018      \t2              \t大学体育（篮球）       \t必修             \t0.75           \t   2.10        \t71             \t\n2017-2018      \t2              \t电工电子技术基础AⅡ     \t必修             \t3.5            \t   2.30        \t73             \t\n2017-2018      \t2              \t电工电子技术基础实习A    \t必修             \t1              \t   2.50        \t中              \t\n2017-2018      \t2              \t中国文化英语         \t必修             \t2              \t   3.20        \t82             \t\n2017-2018      \t2              \t数据库原理与技术B      \t选修             \t3              \t   4.10        \t91             \t\n2017-2018      \t2              \t数据库原理与技术实习B    \t选修             \t1              \t   3.50        \t良              \t\n2017-2018      \t2              \t思辨与创新（网络课程）    \t选修             \t2              \t   3.90        \t89             \t\n2017-2018      \t2              \t用经济学智慧解读中国（网络课程）\t选修             \t3              \t   4.80        \t97.97          \t\n\n平均绩点: 3.70\t\t\t总学分绩点: 351.68\t\t\t总学分: 95.00\n\n\n\n等级考试成绩:\n学年        \t学期        \t等级考试名称    \t准考证号      \t考试日期      \t成绩        \t听力成绩      \t阅读成绩      \t写作成绩      \t综合成绩      \t\n2016-2017 \t1         \t英语四级      \t330391162105502\t2016-12-17\t563       \t212       \t190       \t161       \t0         \t\n2016-2017 \t2         \t英语六级      \t330391171213315\t2017-6-17 \t434       \t112       \t195       \t127       \t0         \t\n2017-2018 \t1         \t英语六级      \t330391172204918\t2017-12-16\t415       \t135       \t151       \t129       \t0         \t\n"
  }
]