记录一下写爬虫踩过的坑
python3 转码问题
python3 中有两种数据类型 string 和 byte ,其中 string 是 Unicode 码,而 byte 是编码后的字节码。平时使用的 encode 就是将 Unicode 码编码为字节码,同理,decode 就是将 byte 数据解码成 Unicode 码数据。有时会出现这种情况,请求到的数据本身就是 Unicode 码数据,这时直接打印出来汉字就会是乱码。这时可以利用 text.encode('unicode_escape_').decode('unicode_escape')
来进行解决,网上查到的解决方法并不全面,总结起来就是 unicode_escape
编码会将字符的内存编码直接存储和读取。此外,在执行上述操作时需要先查看 text 的类型,如果是 str 型,则执行如上操作,如果是 byte 型 则是text.decode('unicode_escape')
。
有时,在执行完如上操作后打印 text 的值会报 utf-8 编码类型的错误,搞不清原因。不过在对 text 进行 json.dumps(test)
操作后就一切正常了,搞不清。。。
python 多进程
python 多进程主要使用 multiprocessing
库来实现,这里面有好几种多进程的实现方法,在这里主要介绍利用 Pool 类创建进程池的方法。其实现代码如下:
1 | from multiprocessing import Pool |
pool.join()
的作用是让主进程在子进程之后结束,防止因主进程结束而导致子进程一起关闭。其中 pool.join()
必须在 pool.close()
之后,否则会没有效果。此外,args 参数传递的是进程运行的参数,这是一个元组,且最后一个元素之后必须加上一个 ,
否则进程也无法运行
进程池有两种进程切换方式,apply 是阻塞式的,这种方式添加必须要等前一个进程的所有子进程结束之后才会切换到另一个进程,而 apply_async 是异步非阻塞式的,也就是说这种方式不会等待当前进程完全结束才切换到另一个进程,而是当完全根据系统调度进行抢占式的进程切换。当进程在等待网络请求或其他操作暂时用不到 cpu 时,就会被挂起,切换到另一个进程,得到响应时再切换回来。总的来说非阻塞式的切换方式效率更高一些。
多进程可以有效提升程序的工作效率,但也会有一些麻烦的方面,比如说在编写爬虫时会有切换 ip 的操作。如果不加干预,每个进程都会切换一次 ip ,这将会十分浪费 ip 资源。所以我们要做的就是让这个切换 ip 的操作只进行一次,我的想法是利用进程间通信控制一个进程共享的全局变量,当需要切换 ip 时,更改该全局变量的值,同时将更换 ip 的方法加上进程锁,当更改完成之后立刻将全局变量复原,从而使后续进程避开 切换 ip 的操作。还有一种方法是将全局变量替换为对外部环境的判断语句,比如说待爬取页面是否能正常访问,若能正常访问则说明 ip 已经切换成功。我实际上用的就是第二种方法。
接下来再说下进程锁的使用,普通的进程锁是不能作为参数传递的,但如果使用的话 python 并不会报错,这时你可能以为是前面的某个方法写错的,你没准还回去 debug 但你不会想到的时,你的程序进程其实压根就没有执行。如果想要在进程之间传递锁,则必须要引入 multiprocessing库中的 Manager 类,具体使用方法如下:
1 | from multiprocessing import Pool,Manager |
补充一点:多进程在出错时会被停掉,但却不会创建新的进程,所以会导致进程越来越少,速度越来越慢,最可怕的情况就是整个程序都有可能会停止,然而由于多进程的原因并不会有报错信息,坑死人。所以使用多进程前最好确保所有会导致程序不正常退出的情况都已经被捕捉到并做好了相应的处理。当然,也会有其他更好的解决方法,但技术菜还没有实现,先做此记录