爬虫速度优化、Python多线程、adsl拨号解决ip被封问题

爬虫速度优化

  1. 优化硬盘存储:每个网页大概多大,加起来以后会有多大,需不需要压缩存储
  2. 优化内存,url去重:减少所有url放在一起去重时,内存不够用情况,使用bloomFilter算法,查询效率高
  3. 反抓取访问频率限制:
    • 研究网站的反爬策略
    • 多ip抓取:IP代理池和adsl拨号
      • IP代理池:比较贵
      • ADSL拨号:便宜,但速度可能稍微慢一些
  4. 网络性能,抓取技术细节调优
    • 开多个线程,探索多长时间/多少频率切换拨号ip最优:
      • 需要对网站的反爬策略进行测试。先开一个线程,一直抓到ip被屏蔽,记录下抓取耗时,总抓取词数和成功抓取词数。再开两个线程/4个线程,重复上面的步骤,统计抓取极限和细节调优的表格。
      • 开线程由于有全局锁的存在,其实还是串行的,所以速度上没有太大的优势。使用多进程。
      • 单个ip、单个cookies多测几次,得到一个大概的值,再进行优化。
    • requests请求优化:设置超时放弃时间requests.get(url, timeout=(5, 10)),二次请求可能比一直在等待的抓取效率高一些
    • 优化ADSL拨号等待时间:
      • 每次断开拨号后,要等待几秒钟再拨号,太短时间内拨号有可能又拨到上一个ip或者失败,所以要等待几秒钟再拨。
      • 拨完号后,需要检测一下外网是否连通,使用ping功能或requests检测外网的连通性以及代理的可用性。
  5. 注意事项:
    • 要计算对方的带宽压力,不要抓取的太过分,以致于影响对方网站的正常运转。
需要了解的一些东西
  1. 多线程和多进程分别是什么?有什么的区别?有什么优缺点?分别适用于什么场景?怎么用?
    1. 多线程和多进程是不一样的!一个是 thread 库,一个是 multiprocessing 库
    2. 多线程
      1. GIL的全称是Global Interpreter Lock(全局解释器锁), 某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。在Python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是Python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。
      2. CPU密集型代码(各种循环处理、计数等等),在这种情况下,由于计算工作多,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。
      3. IO密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。
      4. 而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。
      5. 多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低
    3. 多进程
      1. 每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。所以在这里说结论:多核下,想做并行提升效率,比较通用的方法是使用多进程,能够有效提高执行效率。
    4. 总结
      1. 多线程适用于单核机器、IO密集型代码;多核下,想要并行提高效率,一般用多进程。
  2. 自己没有做过的是URL降重和爬取质量的检测,这个之后要完善一下。
资料
  1. 使用adsl拨号服务器搭建代理池github精简操作地址另外一篇adsl参考
  2. 知乎关于反爬虫内容,主要看那个猿人学写的 adsl拨号方法获取ip启蒙文章。
  3. 进程与线程:
    1. 崔庆才 多进程用法异步协程多进程爬虫实战爬虫系列教程
    2. 100天Python系列进程、线程、协程
    3. 这个博客写的很清晰,补充材料
  4. 多进程问题:
    1. 捕捉进程中出现的异常get方法是可以将异常捕捉到,但是加了get()方法,程序就变成了阻塞的了,进程之间需要串行了,不是我们想要的结果
    2. get获取pool.apply_ssync()的结果的过程,最好放在进程池回收之后进行,避免阻塞后面的语句但是这样处理会使得异常在所有的进程都运行完了以后处理。这种办法适用于需要对多个进程的结果进行处理(加减乘除之类的),而不适用于处理异常,将程序停下来这种需求。
    3. multiprocessing pool 源码分析异常处理过程
    4. 最好的解决办法应该是,明确为什么这一步会出异常,并进行处理,进程不能在半路上停下来,在最终所有任务跑完以后再一起处理。
  5. 其他
    1. [requests 库用法](https://cuiqingcai.com/2556.html)
    2. [ubuntu安装与测试redis](https://www.cnblogs.com/wxjnew/p/9189191.html)
异常处理
  • 抓到异常应该把它抛到调用方法中去解决不是所有异常你都可以处理的,许多异常你就应该把它抛出到调用方去,如果你捕获住一个异常不往外抛,你就等于告诉调用方前面的过程没问题,可以继续往下走,但如果真的出现了问题,继续做下去往往会导致更严重的后果,通常都要比通过异常中止整个过程要糟糕。
redis 操作

sudo service redis-server restart
redis-cli
service redis status

vim 操作
  1. i 插入
  2. esc 推出编辑状态
  3. :q 退出;:wq 保存后退出;:q! 退出且强制不保存
  4. G 最后一行;nG 移动到第n行;0 这一行最前面的字符;$ 这一行最后面的字符;
  5. 搜索功能:/word 向下查找word这个单词;n 接着往下查找下一个word;N 反向查找word单词
Todo
  1. 更优雅的异常信息,使用logging,代替print

猜你喜欢

转载自www.cnblogs.com/YeEn/p/11707727.html