python多线程爬取大量数据,python3多线程爬虫

  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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: