侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Python 爬虫学习笔记之多线程爬虫

Python  /  管理员 发布于 7年前   171

XPath 的安装以及使用

1 . XPath 的介绍

刚学过正则表达式,用的正顺手,现在就把正则表达式替换掉,使用 XPath,有人表示这太坑爹了,早知道刚上来就学习 XPath 多省事 啊。其实我个人认为学习一下正则表达式是大有益处的,之所以换成 XPath ,我个人认为是因为它定位更准确,使用更加便捷。可能有的人对 XPath 和正则表达式的区别不太清楚,举个例子来说吧,用正则表达式提取我们的内容,就好比说一个人想去天安门,地址的描述是左边有一个圆形建筑,右边是一个方形建筑,你去找吧,而使用 XPath 的话,地址的描述就变成了天安门的具体地址。怎么样?相比之下,哪种方式效率更高,找的更准确呢?

2 . XPath 的安装

XPath 包含在 lxml 库中,那么我们到哪里去下载呢? 点击此处 ,进入网页后按住 ctrl+f 搜索 lxml ,然后进行下载,下载完毕之后将文件拓展名改为 .zip ,然后进行解压,将名为 lxml 的文件夹复制粘贴到 Python 的 Lib 目录下,这样就安装完毕了。

3 . XPath 的使用

为了方便演示,我利用 Html 写了个简单的网页,代码如下所示(为了节省时间,方便小伙伴们直接进行测试,可直接复制粘贴我的代码)

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>Test Html</title></head><body><div id="content">  <ul id="like">    <li>like one</li>    <li>like two</li>    <li>like three</li>  </ul>  <ul id="hate">    <li>hate one</li>    <li>hate two</li>    <li>hate three</li>  </ul>  <div id="url">    <a href="http://www.baidu.com">百度一下</a>    <a href="http://www.hao123.com">好123</a>  </div></div></body></html>

用谷歌浏览器打开这个网页,然后右击,选择检查,会出现如下所示界面

这个时候你鼠标右击任何一行 html 代码,都可以看到一个 Copy,将鼠标放上去,就可以看到 Copy XPath ,先复制下来,怎么用呢?

# coding=utf-8from lxml import etreef = open('myHtml.html','r')html = f.read()f.close()selector = etree.HTML(html)content = selector.xpath('//*[@id="like"]/li/text()')for each in content:  print each

看看打印结果

like onelike twolike three

很显然,将我们想要的内容打印下来了,注意我们在 xpath() 中使用了 text() 函数,这个函数就是获取其中的内容,但是如果我们想获取一个属性,该怎么办?比如说我们想得到 html 中的两个链接地址,也就是 href 属性,我们可以这么操作

content = selector.xpath('//*[@id="url"]/a/@href')for each in content:  print each

这个时候的打印结果就是

http://www.baidu.comhttp://www.hao123.com

看到现在大家大概也就对 xpath() 中的符号有了一定的了解,比如一开始的 // 指的就是根目录,而 / 就是父节点下的子节点,其他的 id 属性也是一步一步从上往下寻找的,由于这是一种树结构,所以也难怪方法的名字为 etree()。

4 . XPath 的特殊用法

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>Title</title></head><body><div id="likeone">like one</div><div id="liketwo">like two</div><div id="likethree">like three</div></body></html>

面对上面的一个网页,我们应该如何获取到三行的内容的 ? 嗯哼,很简单,我写三个 XPath 语句不就好了,so easy 。 如果真是这样,那么我们的效率好像是太低了一点,仔细看看这三行 div 的 id 属性,好像前四个字母都是 like, 那就好办了,我们可以使用 starts-with 对这三行进行同时提取,如下所示

content = selector.xpath('//div[starts-with(@id,"like")]/text()')

不过这样有一点麻烦的地方,我们就需要手动的去写 XPath 路径了,当然也可以复制粘贴下来在进行修改,这就是提升复杂度来换取效率的问题了。再来看看标签嵌套标签的提取情况

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>Title</title></head><body><div id="content">  <div id="text">    <p>hello      <b> world        <font color="#ffe4c4">          Python        </font>      </b>    </p>  </div></div></body></html>

像上面这样的一个网页,如果我们想获取到  hello world Python 语句,该怎么获取呢?很明显这是一种标签嵌套标签的情况,我们按照正常情况进行提取,看看结果如何

content = selector.xpath('//*[@id="text"]/p/text()')for each in content:  print each

运行之后,很遗憾的,只打印出了 hello 字样,其他字符丢失了,该怎么办呢?这种情况可以借助于 string(.)如下所示

content = selector.xpath('//*[@id="text"]/p')[0]info = content.xpath('string(.)')data = info.replace('\n','').replace(' ','')print data

这样就可以打印出正确内容了,至于第三行为什么存在,你可以将其去掉看看结果,到时候你自然就明白了。

Python 并行化的简单介绍

有人说 Python 中的并行化并不是真正的并行化,但是多线程还是能够显著提高我们代码的执行效率,为我们节省下来一大笔时间,下面我们就针对单线程和多线程进行时间上的比较。

# coding=utf-8import requestsfrom multiprocessing.dummy import Pool as ThreadPoolimport timedef getsource(url):  html = requests.get(url)if __name__ == '__main__':  urls = []  for i in range(50, 500, 50):    newpage = 'http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=' + str(i)    urls.append(newpage)  # 单线程计时  time1 = time.time()  for i in urls:    print i    getsource(i)  time2 = time.time()  print '单线程耗时 : ' + str(time2 - time1) + ' s'  # 多线程计时  pool = ThreadPool(4)  time3 = time.time()  results = pool.map(getsource, urls)  pool.close()  pool.join()  time4 = time.time()  print '多线程耗时 : ' + str(time4 - time3) + ' s'

打印结果为

http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=50http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=100http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=150http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=200http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=250http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=300http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=350http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=400http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=450单线程耗时 : 7.26399993896 s多线程耗时 : 2.49799990654 s

至于以上链接为什么设置间隔为 50,是因为我发现在百度贴吧上没翻一页,pn 的值就会增加 50。 通过以上结果我们发现,多线程相比于单线程效率提升了太多太多。至于以上代码中多线程的使用,我就不再过多讲解,我相信只要接触过 Java 的人对多线程的使用不会陌生,其实都是大差不差。没有接触过 Java ?那就对不起了,以上代码请自行消化吧。

实战 -- 爬取当当网书籍信息

一直以来都在当当网购买书籍,既然学会了如何利用 Python 爬取信息,那么首先就来爬取一下当当网中的书籍信息吧。本实战完成之后的内容如下所示

在当当网中搜索 Java ,出现了89页内容,我选择爬取了前 80 页,而且为了比较多线程和单线程的效率,我特意在这里对二者进行了比较,其中单线程爬取所用时间为 67s,而多线程仅为 15s 。

如何爬取网页,在上面 XPath 的使用中我们也已经做了介绍,无非就是进入网页,右击选择检查,查看网页 html 代码,然后寻找规律,进行信息的提取,在这里就不在多介绍,由于代码比较短,所以在这里直接上源代码。

# coding=utf8import requestsimport reimport timefrom lxml import etreefrom multiprocessing.dummy import Pool as ThreadPoolimport sysreload(sys)sys.setdefaultencoding('utf-8')def changepage(url, total):  urls = []  nowpage = int(re.search('(\d+)', url, re.S).group(1))  for i in range(nowpage, total + 1):    link = re.sub('page_index=(\d+)', 'page_index=%s' % i, url, re.S)    urls.append(link)  return urlsdef spider(url):  html = requests.get(url)  content = html.text  selector = etree.HTML(content)  title = []  title = selector.xpath('//*[@id="component_0__0__6612"]/li/a/@title')  detail = []  detail = selector.xpath('//*[@id="component_0__0__6612"]/li/p[3]/span[1]/text()')  saveinfo(title,detail)def saveinfo(title, detail):  length1 = len(title)  for i in range(0, length1 - 1):    f.writelines(title[i] + '\n')    f.writelines(detail[i] + '\n\n')if __name__ == '__main__':  pool = ThreadPool(4)  f = open('info.txt', 'a')  url = 'http://search.dangdang.com/?key=Java&act=input&page_index=1'  urls = changepage(url, 80)  time1 = time.time()  pool.map(spider, urls)  pool.close()  pool.join()  f.close()  print '爬取成功!'  time2 = time.time()  print '多线程耗时 : ' + str(time2 - time1) + 's'  # time1 = time.time()  # for each in urls:  #   spider(each)  # time2 = time.time()  # f.close()  # print '单线程耗时 : ' + str(time2 - time1) + 's'

可见,以上代码中的知识,我们都在介绍 XPath 和 并行化 中做了详细的介绍,所以阅读起来十分轻松。

好了,到今天为止,Python 爬虫相关系列的文章到此结束,谢谢你的观看。


  • 上一条:
    浅析Python中MySQLdb的事务处理功能
    下一条:
    Python 爬虫学习笔记之单线程爬虫
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客