lxml库使用Xpath语法解析定位网页数据。
lxml库的使用
(1)修正HTML代码
lxml为XML解析库,但也很好的支持了HTML文档的解析功能,这为使用lxml库爬取网络信息提供了支持条件。
(2)读取HTML文件
除了直接读取字符串,Lxml库还支持从文件中提取内容。
(3)解析HTML文件
Xpath语法
Xpath是一门在XML文档中查找信息的语言,对HTML文档也有很好的支持。
(1)节点选择
Xpath使用路径表达式在XML文档中选取节点。节点是通过沿着路径或者step来选取的。
nodename #选取此节点的所有子节点
/ #从根节点选取
// #从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. #选取当前节点
.. #选取当前节点的父节点
@ #选取属性
举例说明:
user_database #选取元素user_database的所有子节点
/user_database #选取根元素user_database。注释:假如路径起始于正斜杠(/),则此路径始终代表到某元素的绝对路径
user_database/user #选取属于user_database的子元素的所有user元素
//user #选取所有user子元素,而不管它们在文档中的位置
user_database//user #选择属于user_database元素的后代的所有user元素,而不管它们位于user_database之下的什么位置
//@attribute #选取名为 attribute 的所有属性
Xpath语法中的谓语用来查找某个特定的节点或者包含某个指定值的节点,谓语被嵌在方括号中。常见的谓语如下:
/user_database/user[1] #选取属于user_database子元素的第一个user元素
//li[@attribute] #选取所有拥有名为 attribute 属性的 li 元素
//li[@attribute = 'red'] #选取所有li元素,且这些元素拥有值为 red 的 attribute 属性
Xpath中也可以使用通配符来选取位置的元素,常用的就是“ * ” 通配符,它可以匹配任何元素节点。
xpath使用技巧
在爬虫实战中,Xpath路径也可以通过浏览器复制得到,同selector选择器中介绍的方法。
当需要进行批量爬取时,类似于BeautifulSoup中的selector()方法删除谓语部分是不可行的。这时的思路为“先抓大后抓小,寻找循环点”。以从糗事百科的网站爬取用户id为例,打开浏览器进行“检查”,通过“三角形符号”折叠元素,找到每个段子完整的信息标签。
(1)首先通过复制构造div标签路径,此时的路径为:
'div[@class="article block untagged mb15"]'
这样就定位到了每个段子信息,这就是“循环点”。
(2)通过谷歌浏览器进行“检查”定位用户ID,复制Xpath到记事本中:
//*[@id="qiushi_tag_121251148"]/div[1]/a[2]/h2
因为第一部分为循环部分,将其删除得到:
div[1]/a[2]/h2
这便是用户ID的信息。
完整获取用户ID的代码如下:
import requests
from lxml import etree
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
url = 'https://www.qiushibaike.com/text/'
r = requests.get(url, headers=headers)
selector = etree.HTML(r.text)
url_infos = selector.xpath('//div[@class="article block untagged mb15"]')
for url_info in url_infos:
user_id = url_info.xpath('div[1]/a[2]/h2/text()')[0] #尾部加上text()获取文本
print(user_id)
image
starts-with()
有时候会遇到相同的字符开头的多个标签:
<li class="tag-1">需要的内容 1</li>
<li class="tag-2">需要的内容 2</li>
<li class="tag-3">需要的内容 3</li>
想同时爬取时,不需要构造多个Xpath路径,通过starts-with()便可获取多个便签内容,接上例,目前糗事百科按上例的方法已经行不通了,看下图:
image可以发现在 **article block untagged mb15 **的后面增加了typs_long、typs_hot、typs_old等,还按上例那样是匹配不到标签的,这时候就可以用到starts-with(),来提取属性类似的标签信息。
代码改变如下:
import requests
from lxml import etree
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
url = 'http://www.qiushibaike.com/text'
r = requests.get(url, headers=headers)
selector = etree.HTML(r.text)
url_infos = selector.xpath('//div[starts-with(@class,"article block untagged mb15")]')
for url_info in url_infos:
try:
user_id = url_info.xpath('div[1]/a[2]/h2/text()')[0] #尾部加上text()获取文本
except IndexError:
user_id = '匿名用户' #此处遇到匿名用户会抛出IndexError
print(user_id)
当遇到标签套标签情况时:
<div class="red">需要的内容 1
<h1>需要的内容2</h1>
</div>
想同时爬取文本内容,可以通过string(.)完成:
from lxml import etree
html2 = '''
<div class="red">需要的内容 1
<h1>需要的内容2</h1>
</div>
'''
selector = etree.HTML(html2)
content1 = selector.xpath('//div[@class="red"]')[0]
content2 = content1.xpath('string(.)')
print(content2) #string(.)方法可用于标签套标签情况
image
网友评论