python多线程爬取大量数据,python3多线程爬虫
文章的前言是1。多重处理库2。多线程爬虫3。案例练习4。案例分析1。网页内容2。每个章节的链接3。每个章节的正文,并返回章节名称和正文4。将每个章节保存在本地。5.序简单的多线程爬虫,因为只有一个进程,一个线程,所以叫做单线程爬虫。单线程爬虫一次只访问一个页面,无法充分利用电脑的网络带宽。一个页面最多也就几百KB,所以爬虫爬一个页面,额外的网速和从请求到源代码的时间都浪费了。如果爬虫能同时访问10个页面,相当于爬行速度提高了10倍。为了实现这个目标,有必要使用多线程技术。
微观层面上的单线程就像宏观层面上的同时做几件事。这种机制对I/O(输入/输出)密集型操作影响不大,但对CPU密集型操作,由于只能使用CPU的一个核心,所以对性能影响很大。因此,当涉及到计算密集型程序时,有必要使用多进程。
爬虫是一个I/O密集型程序,所以使用多线程可以大大提高爬行效率。
1.多重处理库多重处理本身就是Python的多重处理库,用来处理与多重处理相关的操作。但是由于进程之间不能直接共享内存和堆栈资源,而且启动一个新进程的成本要比线程高很多,所以使用多线程抓取比使用多进程更有优势。
多重处理下有一个哑模块,允许Python线程使用多种多重处理方法。
dummy下面有一个Pool类,用来实现线程池。这个线程池有一个map()方法,它允许线程池中的所有线程同时执行一个函数。
判例案件
计算从0到9的每个数字的平方。
#循环
对于范围(10)内的I:
Print(i ** i)也许你的第一反应会是上面那串代码。你就不能循环吗?反正就10个数字!
这种方法当然可以得到结果,但是代码是一个一个计算的,效率不高。而如果使用多线程技术让代码同时计算很多数的平方,就需要使用multiprocessing.dummy来实现:
来自多重处理。虚拟导入池
#平方函数
def calc_power2(数量):
退货数量*数量
#定义三个线程池
池=池(3)
#定义循环次数
origin_num=[x for x in range(10)]
#使用map使线程池中的所有线程“同时”执行calc_power2函数
result=pool.map(calc_power2,origin_num)
Print(f 计算1-10的平方为:{result} )在上面的代码中,定义了一个函数来计算平方,然后初始化了一个有3个线程的线程池。这三个线程负责计算10个数的平方。谁先计算完手中的数字,谁就拿下一个数字继续计算,直到所有的数字都计算完。
在这个例子中,线程池的map()方法接收两个参数,第一个参数是函数名,第二个参数是一个列表。注意:第一个参数只是函数名,不能用括号括起来。第二个参数是一个iterable对象,这个iterable对象中的每个元素都将被函数clac_power2()作为参数接收。除了list,tuple,set或者dictionary都可以作为map()的第二个参数。
返回页首
2.多线程爬虫因为爬虫是一个I/O密集型的操作,特别是在请求网页源代码的时候,会浪费大量的时间等待网页返回。因此,将多线程技术应用于爬虫,可以大大提高爬虫的运行效率。
下面两段代码用于比较单线程爬虫和多线程爬虫在抓取CSDN首页时的性能差异:
导入时间
导入请求
来自多重处理。虚拟导入池
#自定义功能
定义查询(url):
请求. get(url)
start=time.time()
对于范围内的I(100):
查询( https://www.csdn.net/)
end=time.time()
Print(f 单线程循环访问CSDN 100次,耗时:{end-start} )
start=time.time()
url_list=[]
对于范围内的I(100):
URL _ list . append( https://www . csdn . net/)
池=池(5)
pool.map(查询,url_list)
end=time.time()
Print(f5线程访问CSDN 100次,需要时间:{end-start} )
从运行结果可以看出,一个线程耗时约69.4s,五个线程耗时约14.3s,约为单线程时间的五分之一。还可以及时看到五个线程“同时运行”的效果。
但并不意味着线程池越大越好。从上面的结果也可以看出,五个线程的运行时间实际上是一个线程运行时间的五分之一(13.88s)多一点。这个多出来的点其实就是线程切换的时间。这也从侧面反映出Python的多线程在微观层面上还是串行的。
因此,如果线程池设置得太大,线程切换带来的开销可能会抵消多线程带来的性能提升。线程池的大小需要根据实际情况来确定,没有确切的数据。
返回页首
三。案例练习:从https://www.kanunu8.com/book2/11138/,抓取《北欧众神》所有章节的URL,然后通过多线程爬虫爬下每个章节的内容。在本地创建一个“北欧诸神”文件夹,将小说的每一章都保存在这个文件夹中,每一章都保存为一个文件。
进口re
导入操作系统
导入请求
来自多重处理。虚拟导入池
#抓取主网站地址
start _ URL= https://www . kanu nu 8 . com/book2/11138/
获取网页源代码
:param url:网址
:返回:网页源代码
定义获取来源(url):
html=requests.get(url)
return html . content . decode( gbk )#这个网页需要gbk解码才能正常显示中文。
获取每一章的链接,存储在一个列表中并返回。
:param html:目录页源代码
:return:每个章节的链接
def get_article_url(html):
article_url_list=[]
Article_block=re.findall (body(。*?)div ,html,re。S)[0]
article _ URL=re . find all( a href=(\ d *。html)“”,article_block,re。s)
对于article_url中的url:
article _ URL _ list . append(start _ URL)
返回文章_ url _列表
获取每个章节的主体,并返回章节名称和主体。
:param html:正文源代码
:return:章节名称、正文
def get_article(html):
chapter_name=re.findall( h1(。*?)br ,html,re。S)[0]
text_block=re.search( p(。*?)/p ,html,re。s)。组(1)
Text _ block=text _ block.replace( , )#替换网页的空格字符。
text _ block=text _ block . replace( p , )#替换p /p中嵌入的p/p中的p。
返回章节名称,文本块
将每个章节保存在本地。
:param chapter:章节名称,第x章
:param文章:正文内容
:返回:无
定义保存(章、条):
Os.makedirs (Nordic Gods ,exist_ok=True) #如果没有 Nordic Gods 文件夹,则创建一个,如果有,则不执行任何操作
With open(os.path.join(北欧诸神,章节。txt ), w ,编码= UTF-8 )作为f:
f .写(文章)
根据文本URL获取文本源代码,调用get_article函数获取文本内容,最后保存到本地。
:参数url:正文URL
:返回:无
定义查询_文章(url):
article_html=get_source(url)
章节名称,文章文本=获取文章(文章html)
#打印(章节名称)
#打印(文章_文本)
保存(章节名,文章正文)
if __name__==__main__ :
toc_html=get_source(start_url)
toc_list=get_article_url
池=池(4)
Pool.map(查询_文章,目录_列表)返回顶部
四。案例分析1。获取网页内容#抓取主网站地址。
start _ URL= https://www . kanu nu 8 . com/book2/11138/
获取网页源代码
:param url:网址
:返回:网页源代码
定义获取来源(url):
html=requests.get(url)
return html . content . decode( gbk )#这个网页用gbk模式解码并不难,让中文正常显示这部分。主要是指明需要抓取的网站,通过request.get()的请求方法获取网站。当你通过content.decode()获得网页的解码内容时,你实际上获得的是网页的源代码。
返回页首
2.获取每一章的链接
获取每一章的链接,存储在一个列表中并返回。
:param html:目录页源代码
:return:每个章节的链接
def get_article_url(html):
article_url_list=[]
#根据正文锁定各章节的链接区域。
Article_block=re.findall (body(。*?)div ,html,re。S)[0]
#获取每个章节的链接
article _ URL=re . find all( a href=(\ d *。html)“”,article_block,re。s)
对于article_url中的url:
article _ URL _ list . append(start _ URL)
在这里,我们需要获得每一章的链接。我们首先根据正文锁定每一章的链接区域,然后在链接区域中获取每一章的链接,形成列表进行返回。
当你拿到每一章的链接时,你可以发现它们都是以数字开头,以。html通过页面的源代码,所以可以使用常规(\d*。html)匹配:
返回页首
3.获取每个章节的文本并返回章节名称和文本
获取每个章节的主体,并返回章节名称和主体。
:param html:正文源代码
:return:章节名称、正文
def get_article(html):
chapter_name=re.findall( h1(。*?)br ,html,re。S)[0]
text_block=re.search( p(。*?)/p ,html,re。s)。组(1)
Text _ block=text _ block.replace( , )#替换网页的空格字符。
text _ block=text _ block . replace( p , )#替换p /p中嵌入的p/p中的p。
返回chapter_name,这里我们用规律性分别匹配每一章的标题和正文内容:
格式化后:
返回页首
4.在本地保存每一章
将每个章节保存在本地。
:param chapter:章节名称,第x章
:param文章:正文内容
:返回:无
定义保存(章、条):
Os.makedirs (Nordic Gods ,exist_ok=True) #如果没有 Nordic Gods 文件夹,则创建一个,如果有,则不执行任何操作
With open(os.path.join(北欧诸神,章节。txt ), w ,编码= UTF-8 )作为f:
F.write(文章)在这里,获取我们处理过的文章的标题和内容,写入本地磁盘。首先创建一个文件夹,然后打开文件夹,在章节名称的最后存储每章的内容。txt。
返回页首
5.多线程爬网文章
根据文本URL获取文本源代码,调用get_article函数获取文本内容,最后保存到本地。
:参数url:正文URL
:返回:无
定义查询_文章(url):
article_html=get_source(url)
章节名称,文章文本=获取文章(文章html)
#打印(章节名称)
#打印(文章_文本)
保存(章节名,文章正文)
if __name__==__main__ :
toc_html=get_source(start_url)
toc_list=get_article_url
池=池(4)
Pool.map (query_article,TOC _ list)这里query_article调用get_source和get_article函数获取上面分析的内容,然后调用save函数保存到本地。在主入口main中创建了一个线程池,它包含四个线程。
map()方法允许线程池中的所有线程同时执行一个函数。同时,map()方法接收两个参数,第一个参数是函数名,第二个参数是列表。我们需要抓取这里的每一章,所以我们应该遍历章节链接列表(调用get_article_url来获取它)并执行query_article方法来抓取并保存它。
最后运行程序就行了!
返回页首
转载请联系作者获得授权,否则将追究法律责任。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。