第一ppt网站爬虫与acw_sc__v2加密解密

基于scrapy框架,爬取第一ppt网站上的ppt文件。涉及到acw_sc__v2的加密解密,以及爬虫项目执行时遇到的问题处理记录。

1.说在开始

最近折腾了一下爬虫相关的东西,了解了爬虫的发展历史与技术栈,整合到一起方便后期复习查看。

爬虫属于灰色地带,有恶意的有善意的。所以写爬虫相关的东西,还是要遵守基本道德,只取自己所需资源,只取免费资源,不要触碰别人的利益,砸别人饭碗。

一些恶意的爬虫项目,把什么多线程多进程爬虫,协程爬虫,分布式爬取这些手段几乎全部用上,爬取时间间隔也不加限制,恨不得一秒钟对一个网站访问几十万次,这就给别人的网站带来巨大的负担,一些服务器比较低级的小网站,可能瞬间就崩溃了。并且基于scrapy框架,上述的手段实现也比较简单,目前的爬虫难的不是这些,对于各种验证码的处理,也有很多现成的处理措施,比较有意思的还是js逆向,各种加密手段的解密,攻克这些才是比较有成就感的。

在我看来,善意爬虫就像是对于数据的批量处理,只不过这些数据是在网上,不像是在本地处理那样快速方便。一些网站上的图片,文件本来就是供免费下载的,一些爬虫的需求可能仅仅是一个网页页面上的一行文字,所以如果是需要大批量的下载或者文字复制粘贴,人力也可以做到,只不过是浪费时间,而爬虫就是在节省这个时间。所以,只要限制自己的爬取速度,在不伤害别人网站的前提下,提取一些免费资源,爬虫也不是那么的邪恶。

2. Scrapy

实现爬虫的方式有比较多,其中觉得比较有意思的就是scrapy框架。之前学过Java和Springboot相关的东西,知道了框架绝对是个好东西,可以极大的方便开发。此外,对于selenium也比较感兴趣,但是用selenium做爬虫实在是速度慢,而且这玩意儿最初始设计出来就不是为了爬虫,对这玩意儿感兴趣,是因为感觉它和python自带的键盘鼠标控制等脚本化的包结合在一起,可以更加方便的写一个自动化的脚本,或者写一些小工具,还是比较有意思的。

基于上述,后续的爬虫相关,主要还是基于scrapy框架以及xpath的解析方式。

首先必须了解scrapy的框架结构和基本运行逻辑,以下图片便很好的进行了说明:

1999694-20210530192405005-1327443300

上述图片的每个模块的详细,网上有很多教程,理论方面不做过多描述。内部源码感觉也不像Java,python,springboot那样需要深挖,爬虫说到底也就是个工具,没有必要深挖内部逻辑,了解基本框架和运行逻辑,会使用即可。

  • scrapy基本命令(创建完之后,模板中的具体语句说明,见后续项目实战注释)

    # 新建一个项目
    scrapy startproject 项目名
    
    # 定位到项目文件夹下,并生成一个爬虫模板文件,名字和项目文件名尽量一致
    cd 项目名
    scrapy genspider 模板名 起始域名
    
  • 配置文件说明和修改

    # 这三个无需修改,表示项目名包名等等
    BOT_NAME = "ppt"
    SPIDER_MODULES = ["ppt.spiders"]
    NEWSPIDER_MODULE = "ppt.spiders"
    
    #写用户代理,最基本的避免反爬
    USER_AGENT = ""
    
    #ROBOTSTX协议,默认TRUE,但是国内几乎就没有允许爬的网站,设置成false
    ROBOTSTXT_OBEY = False
    
    #默认最大并发量,下载器基于多线程,可以理解为开了多少个线程(可以开小点,避免反爬拉黑)
    CONCURRENT_REQUESTS = 32
    #和上面想法,降低抓取频率,太快容易被拉黑
    DOWNLOAD_DELAY = 3
    
    #cookie允许,默认就是false
    COOKIES_ENABLED = False
    
    #header设置,上述的user-agent也可以写在这里,避免反爬.Cookie也可以放进去
    DEFAULT_REQUEST_HEADERS = {
       "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
       "Accept-Language": "en",
    }
    
    #开启管道,多个管道可以设置优先级,300是默认,数字越小优先级越高
    ITEM_PIPELINES = {
       "PPT.pipelines.PptPipeline": 300,
    }
    
    #日志设置(debug<info<warring<error<critical)
    #等级
    LOG_LEVEL='INFO'(只会显示这个等级及其以上等级的日志)
    #把日志保存,用于后续分析
    LOG_FILE='名字.log'
    
    #乱码处理,设置数据导出的编码('gb18030',默认是utf-8,一般导出为csv或者json时,会设置这个)
    FEED_EXPORT_ENCODING=
    
    #自定义中间件的优先级
    SPIDER_MIDDLEWARES = {
       "PPT.middlewares.PptSpiderMiddleware": 543,
    }
    
  • 总项目文件夹下,新建一个run.py,用于启动项目。位置如下:

    image-20230517164553271

    # 加上split,不然scrapy crawl ppt就被当成了一个参数,而不是三个
    'scrapy crawl ppt'.split()
    

具体各模块的详细说明,和代码详解,见后续项目的注释。

3. 第一PPT网站文件爬取

3.1 项目要求

爬取PPT下载 - 第一PPT (1ppt.com)中所有类别中的第一页中,所有的ppt模板文件。

这是一个多页的爬虫项目

  • 第一页:跳转到下载页面

    • xpath: /html/body/div[4]/div[@class="col_nav clearfix"]/ul/li
    • 提取栏目分类名称:./a/text()
    • 得到栏目分类链接:./a/@href 需要和https://www.1ppt.com/拼接形成具体分类的页面链接

  • 第二页:进入某一个分类页

    • xpath: //ul[@class="tplist"]/li
    • PPT名称:./h2/a/text()
    • 进入PPT详情页链接:./h2/a/@href 需要和https://www.1ppt.com/拼接形成具体分类的页面链接

    image-20230517204858141

  • 第三页:一个分类存在多页面

    • 获取每一个页面的url地址
    • 第一页的地址就是当前分类页面的地址,更新完meta再次放入调度器,不可以过滤
  • 第四页:进入了某一个PPT详情页

    • xpath://ul[@class="downurllist"]/li/a/@href 需要和https://www.1ppt.com/拼接形成具体分类的页面链接
    • 进入下载页链接
  • 第五页:进入下载页面

    • 第一ppt对下载页面进行了加密处理,需要进行加密方法提取,以及写相应的解密程序
    • 修改请求内容,重新访问下载页
  • 第六页:重新进入下载页

    • xpath://ul[@class='downloadlist']/li[@class='c1']/a
    • 提取具体的ppt下载链接,交给项目管道文件处理

3.2 相关代码

该项目的GitHub代码地址

scrapy的模块有好几个,在文档里对每个模块分别说明非常麻烦。具体说明在代码中进行了详细注释。

3.3 相关问题处理记录

  • acw_sc__v2解密,js逆向。详情在代码注释中有详细解释。

    参考链接:cookie 参数 acw_sc__v2 加密分析

    以及提供几个好用的工具链接:

  • 调度器的请求过滤设置

    坑了我很久,由于使用的scrapy框架,所有的请求是要传给调度器的,调度器如果默认设置,会将重复的链接删除掉,避免多次请求。

    这个去重功能是为了避免请求相同的链接,导致爬取的数据出现重复,干扰也浪费空间。但是这个案例特殊在更新cookie之后,需要重新加载下载页面,也就是要重新进行请求。这个请求和重新加载前的页面唯一的区别就是cookie更新为解密之后,生成的cookie,但是url链接并没有发生变化,而调度器的去重仅仅基于sha1的加密算法生成指纹做判断的,而不是看两个请求整体是不是一样,这就导致更新后的请求进入调度器之后,被过滤掉了,所以这一级页面的请求,需要将dont_filter设置成TRUE。

    本项目的多页提取也存在这个问题,在翻页时,第一页的链接不会变化,直接用的父链接。在翻页提取的时候,需要一个循环,但是第一个链接不能变化,并且需要重新请求一次,因为meta的数据需要更新。但是调度器中已经存在了一个相同url的链接,此时再调入一个,会被过滤,此时也需要设置不允许过滤。

    不知道scrapy中能不能直接修改调度器的代码,或者存在什么方法,使得过滤的时候,只要新的数据,过滤掉旧的数据

  • 日志只会在控制台中展示,一旦设置了LOG_FILE,日志就会存入文件,而不在控制台展示

    写一个Log类,新加一个handler,这样日志既可以在控制台输出,也可以在文件中输出

    import logging
    
    
    class Log():
        log = logging.getLogger()
        log.setLevel("INFO")
        # 创建控制台Handler并设置日志级别为DEBUG
        console_handler = logging.StreamHandler()
        console_handler.setLevel("INFO")
    
        # 创建Formatter并将其添加到Handler中
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        console_handler.setFormatter(formatter)
    
        # 将Handler添加到logger中
        log.addHandler(console_handler)
    
        def __init__(self):
            self.log = Log
    
  • open和spider的参数问题

    pipeline中在存入mysql的时候,这两个函数必不可少,确保数据链接和关闭只进行一次。这两个函数得入参必须要存在一个参数,虽然这个参数后续没有用上,但是也不能删掉。这属于低级错误。

4. 写在最后

本项目是我的第一个爬虫实践,真正有意思的还是数据解密的环节,这种攻防才是最有意思的。写爬虫还是得有基本的道德底线,毕竟属于灰色地带,相关的爬虫脚本功能目的,最好还是替代人工进行网页上免费资源的获取,属于自动化工具,而不是恶意攻击和收费资源的爬取,不然真就是“爬虫写的好,监狱进的早”了,共勉。

之前从事代码相关的工作,使用的是java。由于后续从事人工智能,需要使用python,代码这东西还是敲几遍才会有提升,光看教程用处不大,此次爬虫脚本也算是我第一次用python自己写了一个项目。

posted @ 2023-05-19 23:59  DoubleSails  阅读(549)  评论(0)    收藏  举报