从爬图虫入手学习多线程实战


《从爬虫入手学习多线程 原理分析部分》里我们对Python的多线程原理进行了分析,这次我们就直接拿一个爬虫脚本来进行实践。

上一篇的传送门: PORTAL

阅读这篇文章需要的知识基础:

Python多线程,爬虫基础

requests库的基础方法

os库的基础方法和二进制文件写入方式

re库基础方法

正则匹配基础原理

time库基础方法

一.爬虫原理简单分析

我们现在大多数用的网络爬虫都基于第三方的requests库,经过测试,这个的确比原装的urllib库要快一点点(再说我也习惯了)。

爬虫的获取页面行为就类似于我们平常打开浏览器访问网页的行为:触发请求->发请求包给服务器->服务器返回包(简单来讲就是这个步骤,实际上更复杂)。

但爬虫和浏览器的不同点就在于:浏览器会把服务器的返回包解析成我们能看懂的正常的网页,而爬虫只是单纯的接收数据包。

我们可以通过代码提取出服务器返回包内我们需要的信息,对其进行各种我们需要的操作(比如下载,和格式化数据保存)。

总体来说,一个简单的爬虫由以下几个方面构成:

1. 请求器,用于向目标服务器请求并返回数据包。

2. 数据筛选器,用于从数据包中提取我们需要的数据

3. 数据处理器,用于处理筛选器筛选好的数据

基本上来说拥有以上三个组件,一个简单的小小爬虫就做好了。所以记好这三个部分,但是我们的爬虫因为要用到多线程,所以就不止这三个部分啦,还有一个线程调度器。

接下来就是代码实例分析部分了。实际上这也就是个入门还不到的爬虫,用的各种东西也都是最基础的。因为目标网站几乎把我们平常爬取可能遇到的问题(比如各种访问验证)都解决掉了,这才是真正强的大佬。

有兴趣可以看看大佬的网站分析:https://www.oysterqaq.com/archives/850

二.爬虫实例

1.请求器

我个人是很不喜欢一些博客直接贴上代码草草说两句剩下就让自己体会的,所以我会先分析一下源码。但是个人建议还是最后自己对着代码分析一下,因为可能有些地方我感觉很简单就直接略过了。

请求器

爬虫的请求器和requests库密切相关,在这里我们仅仅使用了requests.get()方法,因为不用登陆所以也就没必要使用session()方法。最后就把我们获取到的返回包给赋给了test对象。

requests.get()方法参数如下:

requests.get(url, headers=(), params=())

常用的有:requests.get(url, headers=())

headers就是我们打开浏览器按F12查看能获取到的

headers

如果不懂各项参数的可以自己百度一下,我在这里就不分析了。

但是注意我们应该把headers信息写到一个dict字典里面。

至于代码里的Root_urlurl都要自己去获取,但这不是这篇的目的,如何获取和分析目标网站会在另外的文章里。

最后在分析筛选器之前我要先列举几个对象(这里为test,以后统称test对象)的方法。

test.text:返回headers中的编码解析的结果,可以通过test.encoding = 'gbk'来变更解码方式

test.content:返回二进制结果

test.json():返回JSON格式,可能抛出异常

test.status_code:返回请求状态码,可以用于debug

test.raw:返回原始socket respons,需要加参数stream=True

实际上还有不少,例如下面图中的,如果有需要可以直接调用(所以说要学好英语啊,学好之后看字面意思就能直接写,看官方文档也方便),我在这里就不一一列举了。

各类方法

参考文档:

https://2.python-requests.org//zh_CN/latest/index.html

其实这些东西看着都很简单,但是如果不自己动手写,尝试运用,那么也只是知道而已。永远记住一句话:实践出真知

2. 数据筛选器

这个数据筛选器主要使用了re库,也就是通过正则匹配的方法来获取我们需要的数据,实际上还可以使用Beautiful Soup库,但这里没必要。之后可以re配合Beautiful Soup库更好地收集我们需要的数据。

其实我学了正则之后的经验就是:遇到需要匹配的数据先找数据特征,之后再着手编写表达式。而正则不是抽象的,其实很形象,你给他放上什么匹配条件他就会去匹配(哪怕是一个空格),虽然正则里面的匹配条件很多,但是没必要全都背下来,掌握几个常用的,其余的用到时候再翻手册也行。

正则学习参考:

https://www.cnblogs.com/chuxiuhong/p/5885073.html(这个我感觉写的相当好)

https://www.cnblogs.com/wenwei-blog/p/7216102.html

https://cloud.tencent.com/developer/article/1114817

在线正则匹配测试,贼有用的:

http://tool.oschina.net/regex/

在这个脚本里我只用到了re.compile()re.findall()方法,不会的话就还是去看看我推荐的那三个博客吧。

筛选器

实际上如果你对着代码分析,你就会发现这个筛选器实际上包含了数据收集和数据加工两部分,并且只获取了png格式的图片(jpg格式的还有一个另脚本,略微修改一下就行,我是懒得写判断条件了,再说两个脚本同时跑还是多进程呢)。

你说是吧(笑)

数据收集就是用正则实现的,之后把匹配好的数据存入一个列表里面。

数据加工就是为了把不能用的部分修剪掉,就如下图所示。因为不同的网站会不一样,所以说这个只是学习一下思路和方法就好。

数据加工

数据加工用到的方法就是replace()方法,需要注意引号问题,还有就是这个是用来处理字符串的,注意输入数据的类型。

replace()方法学习:https://www.runoob.com/python3/python3-string-replace.html

但是这样的处理后的URL仍然不能直接使用,需要拼接Root_url。这个需要看情况对待,可能别的网站不需要这样。

总之来说,爬图虫就是要能直接访问到图片的源地址才能进行下载。

3.数据处理器

数据处理器

对于一个爬图虫来说,数据处理就是把图片的数据流保存进入文件中,并且根据图片格式加上合适的后缀名,并且数据流是二进制的(在这个爬图虫里,img就是下载的图片)。

既然涉及到文件的保存,那么就要引入另一个模块:os

OS模块参考文档:https://www.runoob.com/python3/python3-os-file-methods.html

文件读写参考:https://www.cnblogs.com/ymjyqsx/p/6554817.html

别看上面的代码那么多,实际上也就只是创建文件夹和是否成功的各种判断而已,没什么大不了的,千万不要被吓到。

第61行的num = num + 1是为了迭代地给文件命名,之前定义过num = 0

注意:一定要明确文件路径的写的方法,不然就会报错,推荐使用“/”反斜杠

4.线程调度器

线程调度器

总算到多线程部分了,其实如果看过我的上一篇文章,关于多线程部分应该就不会有问题了,这个地方只是实现了一个很简单的多线程,通过四个线程分别下载四个不同界面,自然不存在任务分配问题,除了需要在文件写入的时候加锁以外就没什么了。

但其实这种方法的效率很低,因为没能实现线程的最大化利用,如果有的页面提前下载完了,那么这个线程就闲置了。这个问题在我之后研究进程池的时候获得了解决。多进程真好玩!

5.任务

如果你真的想学写爬虫并且运用多线程的话,不妨试试这个任务:实现线程的最大化利用。

你可以尝试把所有的图片URL放入一个线程池,让线程自己去取得工作然后完成、更可以挑战一下用多个线程下载同一个文件。如果你能把这个弄明白了,那么一定能提升自己对文件操作的理解以及培养自己的编程思维。

6.代码包和自己的一点小感悟

链接:https://pan.baidu.com/s/1JluQHwDaSK4LhnNdYeA4Rg

提取码:q4dv

这里面有多线程和单线程的爬图虫,可以对照着研究一下。之后我不会更新这些脚本,如果出现问题的话可以尝试自己去debug,相信我,这也是很能提高自己的。

其实经过这么一段时间的研究,我也发现一个问题就是,Python的多线程实际上就只是一个美丽的梦而已,就实质性的速度提升来说,很多方面是不如多进程的,甚至在一些任务上还不如单线程。

当然,我也没全盘否定多线程。多线程更适用于网页请求以及I/O读写操作,多进程适用于CPU密集型操作。还是贴一下官方文档吧:

官方文档

可以看到它不是一无是处,只是我们在很多开发场景上暂时用不到,或者说用到了意义不大。

当然,其实最可能的就是我仍然没有能够全面地了解Python的多线程执行,还是太菜了啊。

如果看过我的这两篇文章之后你仍然对Python的多线程或者多进程兴趣浓厚,那么不妨看看官方文档:https://python-parallel-programmning-cookbook.readthedocs.io/zh_CN/latest/chapter1/index.html

列表

哪怕我研究了一段时间也只是揭开了Python的并行计算的冰山一角而已,这也告诫我们不要小看任何一门语言,除非你已经确定自己完全了解了它。永远要保持一颗谦卑的心,然后用自己最大的努力去学好它。

其实还有一点就是,想学好一门语言,最好的办法就是不断去研习官方文档,然后去自己写代码实践。

三.结语

花了这么三四天总算是把这上下两篇写好了,也是耽误了不少时间,中途又对文章修修改改,有些不合适的地方还要斟酌一番。我的理念是:既然写出来了就要对里面的知识负责,草草了事的话不止是对自己的不负责,更是对别人的不负责。如果不是为了让更多人获益的话,那就不用写博客只用写自己能看懂的笔记就行了。

在这里我要再次感谢帮助我的学长和同学,如果没有他们的话,不知道这两篇文章会是个什么样子呢。


文章作者: 丿卟离丶
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 丿卟离丶 !
  目录