内容较长
1. 问题描述
某日,写了一段爬虫代码(如下,爬取糗百图片代码示例),发现一个异常的问题,就是执行代码时,代码会以极快的速度执行完成,但是任何东西都没有爬取下来,经过问题排查,确认为下载方法downloadEngin
内没有执行,我特意在for循环向线程池提交任务处添加了print,执行代码后会print所有提交信息,但是方法downloadEngin
里面的print一条没有输出.
from multiprocessing import Pool
from requests import Session
from lxml import etree
class getData:
def __init__(self):
self.session = Session()
self.headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3766.400 QQBrowser/10.6.4163.400'
}
def downloadEngin(self,downloadUrl,fileName):
print("开始下载",downloadUrl,fileName)
pictureData = self.session.get("http:" + downloadUrl,headers = self.headers).content
with open(fileName + '.jpg',mode="wb") as w:
w.write(pictureData)
def getPage(self,url):
pageData = self.session.get(url,headers = self.headers).content.decode('utf-8')
self.xpath = etree.HTML(pageData)
pictureName = self.xpath.xpath('/html/body/div[1]/div/div[2]/div/div[2]/a/img/@src')
pool = Pool(10)
for i in range(len(pictureName)):
print("提交任务:{}".format(i))
pool.apply_async(self.downloadEngin,args=(pictureName[i],str(i)))
pool.close()
pool.join()
if __name__ == '__main__':
getData().getPage('https://www.qiushibaike.com/imgrank/')
上段代码执行效果:
提交任务:0
提交任务:1
提交任务:2
提交任务:3
提交任务:4
.....
提交任务:22
提交任务:23
提交任务:24
进程已结束,退出代码为 0
问题排查经过
我反复确认了代码写的没有问题,仔细想了一下可能影响到多线程任务执行的问题,想到了变量.
提交任务时传递了两个参数,如下,此处先是统计了列表长度然后生成对应的数字,然后开始提交任务
pool.apply_async(self.downloadEngin,args=(pictureName[i],str(i)))
传递参数为:列表的实际内容和列表所在的下标索引的数字值,并将数字转换为字符串,作为保存的文件名.
我在for循环上面添加输出了这个列表,列表内容没有问题,随后,我将整段列表复制下来,在for循环的上面位置,重新定义了这个变量的内容
# ['xxx','yyy'] 为之前复制的整段列表内容
pictureName = ['xxx','yyy']
然后重新执行代码,惊疑的发现可以运行了.
此处的问题很明显了,我就使用最笨的方法不断的将上面和这个变量相关的代码注释,手动赋值等操作,终于定位到了有问题的两行代码.
上面只是示例,当时文件内容多很多,xpath声明为全局是因为还有其他方法要用它
self.xpath = etree.HTML(pageData)
pictureName = self.xpath.xpath('/html/body/div[1]/div/div[2]/div/div[2]/a/img/@src')
上面是有问题代码,解决方式是删除self,下面是修改后的代码
xpath = etree.HTML(pageData)
pictureName = xpath.xpath('/html/body/div[1]/div/div[2]/div/div[2]/a/img/@src')
然后重新执行,可以了.
关于此问题的两个可能原因
- 如果也是在for循环提交任务中添加了print,并且可以正常输出,那么多半是传递的变量有问题,可以在源代码基础上先在for循环上面固定变量的值重新运行尝试
- 还有一种情况是多进程函数执行了,也就是上面的
downloadEngin
中的第一行print代码可以正常输出,但是依旧没有将爬取的数据保存下来,也就是除去了第一行代码其他还是类似没有执行,这种情况一般为多进程执行的函数中的代码写的有问题,某种情况下,Python创建的子进程执行错误不会将错误抛出,而是直接结束了子进程,在控制台看不到任何错误输出,只能将子进程函数复制出来一点点执行排查了.