怎样安装百度度就是想多了解知识?

2、多线程爬虫Python代码实战了解了线程和进程的基础知识后,下面就来多线程的Python代码实现,以及如何通过多线程完成爬虫任务,提高爬虫效率。2.1 多线程Python基础知识点这一节首先讲解下多线程的基础知识点,为之后的进阶知识打好基础。(1)多线程初步体验如下所示,定义2个函数test1()函数和test2()函数,其中中间都通过time.sleep()强制休息了3秒和2秒。import time
def test1():
print('任务1进行中,任务1持续3秒')
time.sleep(3)
# 此处用time.sleep()强制休息3s
print('任务1结束')
def test2():
print('任务2进行中,任务2持续2秒')
time.sleep(2)
# 此处用time.sleep()强制休息2s
print('任务2结束')这里首先利用常规调用函数的方法来调用函数,并通过time.time()获取当前时间从而通过end_time - start_time打印程序运行的总时间,代码如下:start_time = time.time()
# 初始时间
test1()
# 常规调用函数的方法
test2()
# 常规调用函数的方法
end_time = time.time()
# 结束时间
total_time = end_time - start_time
# 代码运行时间
print("所有任务结束,总耗时为:" + str(total_time))
运行结果如下:任务1进行中,任务1持续3秒
任务1结束
任务2进行中,任务2持续2秒
任务2结束
所有任务结束,总耗时为:5.001791954040527这就是一个传统的单线程操作:test1()和test2()是依次执行的,test1()函数执行完才去执行的test2()函数,但是在test1()通过time.sleep(3)休息的时候,此时完全可以让它去执行test2()中的任务,但是传统的单线程就是得等test1()执行完毕后再执行test2(),效率低下。下面我们通过多线程的方式来执行test1()和test2()函数,代码如下:import threading
# 引入多线程库
start_time = time.time()
# 初始时间
t1 = threading.Thread(target=test1)
# 创建线程1
t2 = threading.Thread(target=test2)
# 创建线程2
t1.start()
# 线程1启动
t2.start()
# 线程2启动
t1.join()
# 等待线程1结束后再执行主线程
t2.join()
# 等待线程2结束后再执行主线程
end_time = time.time()
# 结束时间
total_time = end_time - start_time
# 代码运行时间
print("所有任务结束,总耗时为:" + str(total_time))解释下这里的核心代码:第1行代码通过import threading引入多线程库;第3-4行代码通过threading.Thread(target=函数名)的方式创建2个线程;第5-6行代码通过start()函数分别启动2个线程;第7-8行代码通过join()函数使得执行完子线程后再执行主线程,关于主线程相关知识点稍后在补充知识点中详细讲解,这里先看下此时的运行结果:任务1进行中,任务1持续3秒
任务2进行中,任务2持续2秒
任务2结束
任务1结束
所有任务结束,总耗时为:3.0044543743133545可以看到此时的test1()和test2()函数就不再是依次执行的了,在执行完test1()函数中的第一行print('任务1进行中,任务1持续3秒')进入time.sleep()的时候(此时CPU闲置了,或者说进入到了7.1节提到的IO操作时间),test2()函数便开始启动了。然后test2()函数在time.sleep(2)执行完成后print('任务2结束')结束函数,然后test1()函数在time.sleep(3)执行完成后print('任务1结束')结束函数,最终整个过程耗时3秒左右。可以看到多线程比原来单线程(耗时5秒多)提高了不少效率。补充知识点:join()函数的作用这里讲解下为什么之前代码中的第7-8行代码要写如下内容:t1.join()
t2.join()在多线程任务中,join()函数的作用是:让“主线程”等待“子线程”结束之后才能继续运行。这么来说可能大家还是不太理解,我们通过代码来解释一下,把之前7-8行的代码删除,然后运行代码,此时运行结果如下所示:任务1进行中,任务1持续3秒
任务2进行中,任务2持续2秒
所有任务结束,总耗时为:0.00093841552734375
任务2结束
任务1结束我们发现程序没有等待test1()和test2()函数结束,便打印“所有任务结束,总耗时为:0.000938……”,这是怎么回事呢,这是因为如下图所示,在程序启动的时候,会默认启动一个主线程,可以将它理解为一个t0线程,相对主线程而言,这些其他定义的线程叫作子线程主线程默认不会等待子线程执行完毕,因此当下面的代码前2行子线程代码启动后,主线程直接执行下面的代码,从而过早地打印了“所有任务结束,总耗时为:0.000938……”。t1.start()
# 线程1启动
t2.start()
# 线程2启动
end_time = time.time()
total_time = end_time - start_time
print("所有任务结束,总耗时为:" + str(total_time))而解决这一问题的办法非常简单,就是每次在通过start()函数启动完所有线程后,通过设置join()函数,让“主线程”等待“子线程”结束之后才能继续运行主线程中的代码。在这个案例中,先是t2线程执行完毕(2秒),然后还要等t1线程执行完毕(3秒),又因为他俩几乎是同时开始执行的,所以总共是3秒后,才会执行主程序剩下的代码。此外有时候需求会反过来:希望主线程不要等待子线程,且还要求主线程一结束子线程立马结束,那时候就可以使用setDaemon()守护线程函数,不过这个函数在多线程爬虫里几乎毫无作用,简单了解即可。最后注意,必须先通过start()函数启动所有线程,然后再使用join()函数,而不能像下面这样启动一个线程后便使用join()函数:t1.start()
t1.join()
# 不可以这么写,这么写会得t1先执行完,然后才会执行t
t2.start()
t2.join()如果这样写的话就把多线程又变成了单线程了,因为第2行代码中t1.join()会一直等待线程t1全部执行完,才会继续执行t1.join()之后的t2线程的相关代码(因为之后的代码虽然是启动t2线程,但代码本身也是属于主线程代码,一旦设置了t1.join(),就会先等待线程t1执行完,再继续执行主线程代码)。该知识点也是简单了解即可,实际应用过程中,我们需要记住的就是必须先通过start()函数启动所有线程,然后再使用join()函数。(2)多线程爬虫初体验简单体验了多线程爬虫的用处后,我们通过一个爬虫的案例来体会下多线程的作用,这里首先定义2个爬虫函数test1()函数和test2()函数,如下所示:import time
import requests
import threading
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36'}
def test1():
url = 'https://www.baidu.com/s?tn=news&rtt=4&word=阿里巴巴'
res = requests.get(url, headers=headers).text
print(len(res))
# 可以把这行代码换成3.1节获取源代码/解析和打印标题相关代码
def test2():
url = 'https://www.baidu.com/s?tn=news&rtt=4&&word=贵州茅台'
res = requests.get(url, headers=headers).text
print(len(res))这里主要为了演示多线程代码,为了让代码简洁些,这里每个函数中只是打印了获取到的网页源代码的长度,刚兴趣的读者可以将print(len(res))换成换成3.1节获取源代码/解析和打印标题相关代码。这里首先通过常规方法运行下这2个函数,看下所花的时间,代码如下:start_time = time.time()
test1()
test2()
end_time = time.time()
total_time = end_time - start_time
print("所有任务结束,总耗时为:" + str(total_time))运行结果如下,总耗时约2秒左右,看看到平均爬取一个网页耗时1秒左右。214065
156567
所有任务结束,总耗时为:2.0819427967071533然后启用多线程,代码如下:start_time = time.time()
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join()
end_time = time.time()
total_time = end_time - start_time
print("所有任务结束,总耗时为:" + str(total_time))运行结果如下,可以看到此时的总耗时为0.97秒,可以看到使用2个线程后爬取效率提升了1倍左右。(这个时间会随着各人网络情况有所变化,不一定和书上写的一致)214067
156555
所有任务结束,总耗时为:0.9735295295715332(3)多线程传入参数之前演示的函数里没有参数,如果有参数的话可以在如下代码中通过args参数传入:threading.Thread(target=test1, args=(参数1,参数2,参数3……))演示代码如下:import threading
import time
def test1(x):
print('任务1进行中,此时参数为' + str(x))
time.sleep(3)
# 此处用time.sleep()强制休息3s
print('任务1结束')
def test2(x):
print('任务2进行中,此时参数为' + str(x))
time.sleep(2)
# 此处用time.sleep()强制休息2s
print('任务2结束')
start_time = time.time()
t1 = threading.Thread(target=test1, args=('x1', ))
t2 = threading.Thread(target=test2, args=('x2', ))
t1.start()
t2.start()
t1.join()
t2.join()
end_time = time.time()
total_time = end_time - start_time
print('所有任务结束,总耗时为:' + str(total_time))打印结果如下,可以看到成功传入参数。任务进行中,此时参数为x1
任务进行中,此时参数为x2
任务2结束
任务1结束
所有任务结束,总耗时为:3.0010738372802734有个细节需要注意,args里如果只有一个参数,一定要加一个逗号(写成args=('x1', )),否则就不是一个元组(tuple)格式(元组和列表非常类似,主要区别就是它是用小括号而不是中括号包围,以及元组中内容不可修改),如果是传入多个参数,就不用考虑该问题,正常写成args=(参数1,参数2)即可。这里拿实际的爬虫例子做一个演示,首先定义一个爬虫函数test()函数,此时函数中是有参数:company,代码如下,其含义是为了爬取不同公司的百度新闻新闻。import time
import requests
import threading
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36'}
def test(company):
url = 'https://www.baidu.com/s?tn=news&rtt=4&word=' + company
res = requests.get(url, headers=headers).text
print(len(res))
# 可以把这行代码换成3.1节获取源代码/解析和打印标题相关代码然后通过多线程爬取多个公司的百度新闻,代码如下:companys = ['阿里巴巴', '华能信托', '京东', '腾讯', '京东']
start_time = time.time()
thread_list = []
# 创建一个空列表,用来存储每一个线程
for i in range(len(companys)):
t = threading.Thread(target=test, args=(companys[i],))
thread_list.append(t)
for i in thread_list:
i.start()
for i in thread_list:
i.join()
end_time = time.time()
total_time = end_time - start_time
print("所有任务结束,总耗时为:" + str(total_time))第1行代码是要爬取的公司列表;第4-7代码代码为核心代码,这里的写法也和之前有所不同,因为我们事先并不清楚有多少个公司需要爬取,因此这里首先第4行代码创建了以空列表,用来存储每一个线程;然后第5-第7行代码遍历公司列表,针对每一个公司创建一个个独立的线程,并通过args=(companys[i],)传入公司名称,最后通过thread_list.append(t)将每个线程存放到线程列表thread_list中。第9-10行代码为启动各个线程的代码;第12-13行代码给每个线程添加join()函数,注意不可以把9-13行代码合并写成如下格式:for i in thread_list:
i.start()
i.join()因为在本节第(1)部分的补充知识点最后讲过如果这样设置的话,会把多线程又变成了单线程,因为这样使用join()函数的话会导致每个线程执行完毕后才会执行之后的线程。正确的做法是先通过start()函数启动所有线程,然后再使用join()函数。最终的运行结果如下所示:269206
214220
269210
213654
151082
所有任务结束,总耗时为:0.9554851055145264可以看到通过5个线程爬取5个公司所花的时间和之前爬取一个公司所花的时间几乎一致,所以可以说这5个爬虫任务几乎是同时启动&同时完成的,爬虫效率获得了非常大的提升,这也是多线程爬虫的主要意义。上面这个代码其实还不太智能,因为倘若有1000个公司需要爬取,那么不可能说是启动1000个线程,每个线程用完就不用了,这样其实也是对资源的一种浪费,而且计算机性能有限,就算爬虫主要是IO操作,也不太可能可以同时运行1000个爬虫项目。合理的代码编写逻辑应该是创建固定数量的线程个数(例如5-10个),然后把这1000个网页分配给各个线程去爬取,哪个线程空闲了就去爬取没爬的网页,这个具体操作我们将在下1小节讲解。补充知识点:timeout参数防止子线程卡住此外有的时候网络请求会卡住,因此设置timeout参数(其含义为如果n秒后还未访问成功就弹出超时报错),防止子线程卡死导致主线程也一直卡着不动,然后通过try except避免程序报错终止(如果不设置try except,出现超时后程序会提示报错而使得程序终止),如下所示:def test(company):
url = 'https://www.baidu.com/s?tn=news&rtt=4&word=' + company
try:
res = requests.get(url, headers=headers, timeout=10).text
print(len(res))
except:
res = '访问超时'2.2 多线程Python进阶知识点上一小节讲到,我们希望创建固定数量的线程个数(例如5-10个),然后把多个网页分配给各个线程去爬取,那么如果要实现这样的操作,肯定需要有个容器存放这些网址,而且还有满足能够无放回的取出这些网址,也就是取出1个网址后容器里就少1个网址,这样不会重复爬取同一个网址。那么如何构造这么一个容器呢?很多读者可能第一时间会想到使用列表,但是列表是线程不安全的(具体解释可以参考本节的补充知识点),因此我们这里引入一个新的知识点:Queue队列,队列的用法和列表比较类似,而且是线程安全的,适合多线程任务。(1) Queue队列相关知识点使用Queue队列的主要目的有2点:1.写入数据;2.读取数据,这两个功能分别对应队列的两个函数:put()函数和get()函数,下面我们通过一个简单的例子来进行学习:import queue
q = queue.Queue()
# 创建一个空队列
# 通过put()函数写入数据
q.put(0)
q.put(1)
q.put(2)
print(q.queue)
# 输出当前队列所有数据第1行代码引入队列库queue(Python自带,无需安装);第2行代码通过Queue()函数创建一个空队列;第4-6行代码通过put()函数向队列中依次写入数据,注意这先写入的等会也会先被提取;第7行代码打印此时的队列内容,结果如下,可以看到3个数字被成功依次写入:deque([0, 1, 2])将数据存储进队列后,可以通过get()函数提取队列里的内容,代码如下:# 通过get()函数提取队列数据(先进先出)
keyword = q.get()
print(keyword)
# 输出此时队列数据,此时有个数据已经被提取出去了
print(q.queue)第2行代码通过get()提取数据,注意队列提取数据默认是先进先出,也就是先存入的数据(本例为数字0)会被先读取出来,这里将读取出来的数据赋值给keyword;第3行代码打印keyword,第5行代码打印此时的队列,如下所示:0
deque([1, 2])可以看到队列中的内容成功被取出来了,而且是无放回的取出。如果此时再执行一遍q.get(),因为先进先出的特性,数字1将会被取出。此外如果想实现后进先出的话,可以使用LifoQueue队列,实战中用的很少,感兴趣的可以自行查阅。除了put()和get()函数外,关于队列我们还需要知道一个empty()函数,其用法为:队列.empty(),如果队列为空,返回True,反之False,举例来说,继续执行上面的代码:# 查看此时队列是否为空
print(q.empty())
# 把队列里剩下的内容取光,然后再查看队列是否为空
q.get()
q.get()
print(q.empty())第2行代码查看此时的队列是否为空,因为此时队列里还有内容deque([1, 2]),所以会返回Flase;然后第4-5行代码把剩下的2个数页取出来(这里为了快速演示,并没有把取出来的数赋值给某个变量);第6行代码打印此时队列是否为空的情况,因为都被取光了,所以返回False,代码运行结果如下所示:False
True(2)队列的初步实战应用了解了队列的基本知识后,我们来看下队列的初步实战应用,代码如下:companys = ['阿里巴巴', '贵州茅台', '格力电器', '中兴通讯']
url_queue = queue.Queue()
# 创建一个空队列
for company in companys:
url_i = 'https://www.baidu.com/s?rtt=4&tn=news&word=' + company
url_queue.put(url_i)
# 将网址添加到队列中
while not url_queue.empty():
# 当队列里还有内容时,就执行下面的内容
url = url_queue.get()
print(url)第1行代码是要爬取的目标公司名称;第2行代码创建一个空队列url_queue,用来存储等会构造的网址url;第3-5行代码通过for循环遍历目标公司,url_i通过字符串拼接的方式拼接各个公司的百度新闻网址,最后通过put()函数把各个公司的百度新闻网址填入url_queue队列中;第7行代码比较核心,其含义就是当队列里还有内容时,就一直执行下面的内容,因为当队列还有内容的时候,url_queue.empty()的值为False,那么while
not False的含义其实就是while True的意思,因此会一直执行下面的代码,直到队列里所有内容都被取光为止;第8行代码则是通过get()函数无放回地取出url_queue中的各个网址;第9行代码打印此时提取的网址,结果如下:https://www.baidu.com/s?rtt=4&tn=news&word=阿里巴巴
https://www.baidu.com/s?rtt=4&tn=news&word=贵州茅台
https://www.baidu.com/s?rtt=4&tn=news&word=格力电器
https://www.baidu.com/s?rtt=4&tn=news&word=中兴通讯这个代码其实就是为了7.2.3节的综合爬虫案例做铺垫了,有了网址url之后,就可以结合多线程来对网页进行批量爬取了,我们稍后就会讲到。补充知识点:Python中的线程安全问题与GIL锁(简单了解即可)这里简单介绍下Python中的线程安全问题与GIL锁的相关概念,因为在笔者在本书的案例实战中都会避免出现线程安全问题,所以这个知识点简单了解即可,推荐直接阅读下一小节。熟悉列表(list)操作的读者可能会有疑惑,通过append()函数添加内容,然后通过pop()函数取出列表里的内容,代码如下,其执行效果和队列差不多,主要区别就是列表取数是后进先出,而不是像队列取数一样先进先出。a = []
a.append(0)
a.append(1)
a.append(2)
print(a)
# 该打印结果为:[0, 1, 2]
keyword = a.pop()
# pop()函数可以提取列表中的内容
print(keyword)
# 该打印结果为2,它提取的是最后一个元素
print(a)
# 此时列表为[0, 1],看上去和使用队列Queue效果差不多那么为什么还需要使用队列Queue呢,这是因为列表是线程不安全的,而队列Queue是线程安全的,线程是否安全,针对的不是对象,而是操作,像a.append(i)这样的操作其实是线程安全的(经过笔者实践append()和pop()操作效果和用队列Queue的put()和get()几乎没有区别)。如果指这样的操作a[0] = a[0] + 1,它不是一个原子操作(就是说这个操作其实是由多个操作合成的),不加以保护就会导致线程不安全。举个list线程不安全的例子,代码如下:import threading
import time
a = [10]
# 初始列表,里面只有1个元素10
def change():
global a
# 因为函数内容要对全局变量做修改,所以得通过global声明全局变量
for i in range(1000000):
# 下面的操作执行1000000次
a[0] = a[0] + 1
# 第一个元素+1
a[0] = a[0] - 1
# 第一个元素-1
t1 = threading.Thread(target=change)
t2 = threading.Thread(target=change)
t1.start()
t2.start()
t1.join()
t2.join()
print(a)理想状态下,因为函数的内容是先+1然后再-1,按理说最后输出的a应该还是原来的[10],但是现实运行结果却是每次运行出来的结果都不一样,这是因为zero[0] = zero[0] + 1这个操作不是原子性的,分解下来如下所示:x = a[0] + 1
a[0] = x因为这里是2个步骤,因此很有可能在执行第一个步骤:赋值给x完后,GIL锁就给了其他线程,然后a[0] = x这一步骤就没来得及执行,导致数据混乱,产生线程不安全的情况。具体解释下GIL锁,GIL锁是Python中实现多线程时会遇到的一个概念,其含义为对于单个CPU而言,某一时点只能执行1个线程的CPU操作,而且会分配给这个线程一个GIL锁,可以将其理解为线程可以运行的工作许可证,当A线程执行完一个CPU操作后(比如x = a[0] + 1),还没来得及执行下一步操作(例如a[0] = x),在CPU的调度算法下,可能就会提前交出这个GIL锁(工作许可证)给另外一个线程B,从而导致数据通讯混乱,线程不安全。那如何来保证线程安全呢,最好的办法就是尽量不要在不同线程间共用数据,像这里的列表a,就是一个全局变量,因为函数要修改全局变量,还得在函数中通过global()函数声明a为全局变量。但是现实案例中可能有时就得修改这个全局变量,那么如何来做到线程安全呢?第二种办法就是使用线程锁,代码如下,不过实际代码编写中还是应当尽量避免出现共用数据的情况。import threading
import time
a = [10]
lock = threading.Lock()
# 加上1把线程锁
def change():
global a
for i in range(1000000):
with lock:
a[0] = a[0] + 1
a[0] = a[0] - 1
t1 = threading.Thread(target=change)
t2 = threading.Thread(target=change)
t1.start()
t2.start()
t1.join()
t2.join()
print(a)在第5行和第10行代码,加上一把锁就行,强行保证了操作的原子性(也就是保证了a[0] = a[0] + 1在执行它的2个步骤的时候不会被打断),但是强行阻止CPU进行线程调度会降低执行效率,甚至会导致多线程的执行时间比单线程还要久,感兴趣的读者可以用7.2.1节的相关方法加start_time和end_time查看发现加锁后虽然的确执行效果a的结果为[10],但是执行时间却花了2秒左右,而如果通过常规方法执行两次change()函数,所花费的时间也只有1秒。所以总的来说,还是那句话,在多线程任务中,尽量不要共用数据。总结来说,在传统Python多线程任务中,之所以使用队列Queue,而不用列表list,就是担心a[0] = a[0] + 1这种非原子操作可能产生的线程不安全,而如果人为的加锁有可能导致效率下降,而Python的Queue库,提供了线程同步机制(这个机制就类似上面说的上锁的这个流程),可以直接使用队列轻松实现多线程同步,该知识点简单了解即可,只需记住在多线程任务中,优先推荐使用队列Queue来存储和调用数据。https://www.freebuf.com/news/162019.html2.3 案例实战 - 多线程爬取百度新闻本节进行多线程爬取百度新闻案例实战,首先引入相关库:import queue
import requests
import threading
import re
import time
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36'}然后通过如下代码将目标公司的相关百度新闻网址添加到队列url_queue中。companys = ['阿里巴巴', '贵州茅台', '格力电器', '中兴通讯', '五粮液', '腾讯', '京东']
url_queue = queue.Queue()
# 创建一个空队列
for company in companys:
url_i = 'https://www.baidu.com/s?rtt=4&tn=news&word=' + company
url_queue.put(url_i)
# 将网址添加到队列中然后定义爬虫函数,这里的核心是第2-3行代码,利用7.2.2节相关知识点,会一直从队列中取数据一直到取完队列里的网址为止;第4-9行代码为3.1节相关爬取&解析网页代码。def crawl():
while not url_queue.empty():
# 当队列里还有内容时一直执行
url = url_queue.get()
# 提取队列中的网址(先进先出)
res = requests.get(url, headers=headers, timeout=10).text
p_title = '<h3 class="news-title_1YtI1">.*?>(.*?)</a>'
title = re.findall(p_title, res, re.S)
for i in range(len(title)):
title[i] = re.sub('<.*?>', '', title[i])
print(str(i + 1) + '.' + title[i])最后激活5个线程,这里核心代码在第3-5行,可以看到这里是设置激活了5个线程,而不是7.2.1节有多少网址就激活多少线程。这5个线程会一起执行刚刚定义的crawl()爬虫函数,各自提取url_queue队列中的网址,直到所有网址被提取完毕,也即所有爬虫任务都完成为止:start_time = time.time()
# 起始时间
thread_list = []
for i in range(5):
# 这里激活5个线程,可以自己改成1,看看单线程的时间
thread_list.append(threading.Thread(target=crawl))
for t in thread_list:
t.start()
for t in thread_list:
t.join()
end_time = time.time()
# 结束时间
total_time = end_time - start_time
print("所有任务结束,总耗时为:" + str(total_time))最终运行结果如下所示:1.中兴通讯的革新与蜕变
2.A股:周末,中兴通讯等四家公司发布消息
……
9.京东徐雷:京东是供应链技术公司,颠覆你的可能是你想象不到的玩家
10.京东健康今日在香港成功上市
所有任务结束,总耗时为:1.80404442787170可以看到这里第一条信息并不是companys列表里的第一条信息“阿里巴巴”,这也的确符合多线程的特性,这里5个线程同时启动,所以到底那个线程先爬完并展示相关公司其实的确也是比较随机的。这里的总耗时为1.8秒左右(不同电脑以及网络环境的不同会使这个结果有较大差异),感兴趣的读者,可以把上面第4行代码中的5改成1,看看单线程的运行时间,可以发现单线程需要4秒左右,因此多线程的确可以提高爬虫效率。补充知识点:网络不好&反爬下的多线程爬虫解决办法有的时候网络不好的时候,可能会导致爬虫一直卡着,此时可以通过如下代码设置timeout参数,并通过try except防止超时报错导致代码停止运行。try:
res = requests.get(url, headers=headers, timeout=10).text
except:
res = '访问超时'最后再说一点,多线程爬虫有的时候会被网站视为访问频率过快,从而触发反爬措施,导致获取不到真正网页源代码的结果,如下图所示,因为笔者测试太多次多线程爬取百度新闻,导致触发反爬。笔者打印此时网页源代码(此时的网页源代码还需要通过5.1.3节处理乱码的方式处理下中文乱码),发现网页源代码显示“网络不给力,请稍后重试”,这其实就是百度对我们的爬虫进行了IP反爬,那么此时的解决办法,一是可以等待一段时间(通常几分钟)后再执行代码,二是可以利用第8章即将讲到的IP代理技巧在每次爬取的时候添加IP代理。3、课程相关资源笔者获取方式:微信号获取添加如下微信:huaxz001 。笔者网站:华小智首页王宇韬相关课程可通过:京东链接:[https://search.jd.com/Search?keyword=王宇韬],搜索“王宇韬”,在淘宝、当当也可购买。加入学习交流群,可以添加如下微信:huaxz001(请注明缘由)。《零基础学Python网络爬虫案例实战全流程详解(入门与提高篇)》(王宇韬,吴子湛,等)【摘要 书评 试读】- 京东图书 (jd.com)《零基础学Python网络爬虫案例实战全流程详解(高级进阶篇)》(王宇韬,吴子湛,史靖涵)【摘要 书评 试读】- 京东图书 (jd.com)各类课程可在网易云、51CTO搜索王宇韬,进行查看。}

我要回帖

更多关于 怎样安装百度 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信