前言
上篇文章我们使用了原生正则表达式来解析网页,这次我们使用xpath来解析网页
正文
安装xpath
pip install lxml
开始xpath
-
使用Xpath需要从lxml库中导入etree模块,还需使用HTML类对需要匹配的HTML对象进行初始化。HTML类的基本语法格式如下。
lxml.etree.HTML(text, parser=None, *, base_url=None)
参数名称 说明 text 接收str。表示需要转换为HTML的字符串。无默认值 parser 接收str。表示选择的HTML解析器。无默认值 base_url 接收str。表示设置文档的原始URL,用于在查找外部实体的相对路径。默认为None -
Xpath使用类似正则的表达式来匹配HTML文件中的内容,常用匹配表达式如下。
表达式 说明 nodename 选取nodename节点的所有子节点 / 从当前节点选取直接子节点 // 从当前节点选取子孙节点 . 选取当前节点 … 选取当前节点的父节点 @ 选取属性 -
Xpath中的谓语用来查找某个特定的节点或包含某个指定的值的节点,谓语被嵌在路径后的方括号中,如下。
表达式 | 说明 |
---|---|
/html/body/div[1] | 选取属于body子节点下的第一个div节点 |
/html/body/div[last()] | 选取属于body子节点下的最后一个div节点 |
/html/body/div[last()-1] | 选取属于body子节点下的倒数第二个div节点 |
/html/body/div[positon()<3] | 选取属于body子节点下的下前两个div节点 |
/html/body/div[@id] | 选取属于body子节点下的带有id属性的div节点 |
/html/body/div[@id=”content”] | 选取属于body子节点下的id属性值为content的div节点 |
/html /body/div[xx>10.00] | 选取属于body子节点下的xx元素值大于10的节点 |
- Xpath中还提供功能函数进行模糊搜索,有时对象仅掌握了其部分特征,当需要模糊搜索该类对象时,可使用功能函数来实现,具体函数如下。
功能函数 | 示例 | 说明 |
---|---|---|
starts-with | //div[starts-with(@id,”co”)] | 选取id值以co开头的div节点 |
contains | //div[contains(@id,”co”)] | 选取id值包含co的div节点 |
and | //div[contains(@id,”co”)andcontains(@id,”en”)] | 选取id值包含co和en的div节点 |
text() | //li[contains(text(),”first”)] | 选取节点文本包含first的div节点 |
了解了xpath,现在开始编写程序
-
需求: 还是和上一篇一样,只是今天我们需要把每篇新闻的文字正文保存到本地
-
我们先用浏览器的调试工具获取一下某个元素的xpath测试一下
- 审查元素后->右键->copy->copy xpath
[外链图片转存失败(img-XGDA9UJY-1564032836940)(https://raw.githubusercontent.com/kevinlu98/cloudimg/master/data/QQ20190725-114955@2x.png)]
得到结果//*[@id="js_index2017_wrap"]/div[2]/div[2]/div[4]/div[1]/div[2]/div/div/div/div[1]/div[1]/div[2]/ul[1]/li[1]/a
我们可以结合上面过于xpath用法的表来看看这个xpath大家能理解不
我们加上text()获取一下标签内的文本//*[@id="js_index2017_wrap"]/div[2]/div[2]/div[4]/div[1]/div[2]/div/div/div/div[1]/div[1]/div[2]/ul[1]/li[1]/a/text()
-
测试程序
# @File: code04.py # @Author: lengwen # @Time: 2019-07-25 11:37 # @Desc: xpath测试程序 import requests from lxml import etree url = 'http://www.163.com' resp = requests.get(url) # 初始化etree 转换为xpath html_etree = etree.HTML(resp.text, parser=etree.HTMLParser(encoding='utf-8')) # 使用xpath获取内容 result1 = html_etree.xpath('//*[@id="js_index2017_wrap"]/div[2]/div[2]/div[4]/div[1]/div[2]/div/div/div/div[1]/div[1]/div[2]/ul[1]/li[1]/a/text()') print(result1)
- 结果
[外链图片转存失败(img-QNMuXDVE-1564032836942)(https://raw.githubusercontent.com/kevinlu98/cloudimg/master/data/QQ20190725-115542@2x.png)]
- 结果
-
接下来进入正题,我们先找到新闻链接的位置,这次我们就不使用浏览器的调试工具直接获取xpath了,因为大家开始看到那个xpath太长了
[外链图片转存失败(img-7vIRt5DM-1564032836943)(https://raw.githubusercontent.com/kevinlu98/cloudimg/master/data/QQ20190725-115955@2x.png)]
- 根据xpath规则和我们昨天找到的共性我们构建出xpath
//div[@class="news_default_yw"]/ul/li/a[starts-with(@href,"https://news.163.com/")]/@href
- 这样我们现在得到了所以新闻的URL
- 然后我们将它保存到本地,方便后面使用
- 编写程序
# @File: code05.py # @Author: lengwen # @Time: 2019-07-25 12:01 # @Desc: xpath正式开始 import requests from lxml import etree url = 'http://www.163.com' resp = requests.get(url) # 初始化etree 转换为xpath html_etree = etree.HTML(resp.text, parser=etree.HTMLParser(encoding='utf-8')) # 构建xpath result_url_list = html_etree.xpath( '//div[@class="news_default_yw"]/ul/li/a[starts-with(@href,"https://news.163.com/")]/@href') # 以写的方式打开url_list.txt这个文件 with open('./url_list.txt', 'w') as f: for i in result_url_list: print(i) f.write(i + "\n")
- 结果
[外链图片转存失败(img-EDJS4MWh-1564032836943)(https://raw.githubusercontent.com/kevinlu98/cloudimg/master/data/QQ20190725-122248@2x.png)]
接下来我们该爬取每个新闻的文字正文了
-
随便点进去一个链接可以看到文章的正文都在
div.post_text>p
中,也就是我们构建xpath渠道div.post_text
下的所有p
的文字就可以了 -
构建xpath
//div[@class="post_text"]/p/text()
-
先看一段调试程序
# @File: code06.py # @Author: lengwen # @Time: 2019-07-25 12:26 # @Desc: 爬取新闻 import requests from lxml import etree with open('./url_list.txt') as f: url = f.readline() resp = requests.get(url) html_etree = etree.HTML(resp.text, parser=etree.HTMLParser(encoding='utf-8')) text = html_etree.xpath('//div[@class="post_text"]/p/text()') for i in text: print(i)
没有注释大家应该也能看懂了
-
结果
[外链图片转存失败(img-YE3RaXgb-1564032836944)(https://raw.githubusercontent.com/kevinlu98/cloudimg/master/data/QQ20190725-123346@2x.png)] -
然后我们只需要加上循环,以及将爬取下来的内容保存就可以了
-
接下来看程序
# @File: code06.py # @Author: lengwen # @Time: 2019-07-25 12:26 # @Desc: 爬取新闻 import requests from lxml import etree import os # 创建一个文件夹用于保存新闻 file_dir = 'news' # 判断文件夹是否存在,不存在则创建 if not os.path.exists(file_dir): os.mkdir(file_dir) with open('./url_list.txt') as f: # 按行读取文件 url = f.readline() while url: resp = requests.get(url) html_etree = etree.HTML(resp.text, parser=etree.HTMLParser(encoding='utf-8')) text = html_etree.xpath('//div[@class="post_text"]/p/text()') # 获取标题作为文件名 title = html_etree.xpath('//title/text()') print(title, '保存完成') with open(os.path.join(file_dir, title[0] + '.txt'), 'w') as fw: for i in text: fw.write(i + '\n') url = f.readline()
-
结果
[外链图片转存失败(img-JXVz9A39-1564032836945)(https://raw.githubusercontent.com/kevinlu98/cloudimg/master/data/QQ20190725-124520@2x.png)]
[外链图片转存失败(img-0bK1HWFL-1564032836946)(https://raw.githubusercontent.com/kevinlu98/cloudimg/master/data/QQ20190725-124540@2x.png)]
关于xpath的部分到此结束