一、xpath
1. 安装xpath
- 下载插件
- 将其拖入谷歌浏览器,此时高版本谷歌浏览器会报这个错误
- 只需要将crx文件后缀改为zip,然后再次拖入即可
- 重启浏览器后,随便打开一个页面,按下快捷键Ctrl+Shift+X或者点击右上角插件图标,就会出现解析框,证明安装成功
2. python安装lxml
pip install lxml
通过pip list命令确保自己安装成功
编写代码测试,from lxml import etree,只要不报错就安装成功,如果报错了,看下面的解决步骤
进入settings,选择Project:项目名,如果发现已经有lxml依然无法使用lxml的话,就需要重新配置interpreter。否则证明你安装的位置错了(可能安装在其它位置,或者其它版本的python中了),或者python版本选错了,也许是pycharm自带的python而不是我们本地的python
- 进入settings,选择Project:项目名,点击add interpreter,选择add local interpreter
- 选择existing,配置interpreter为我们使用的python的python.exe,刚刚我们就是将lxml安装到了这个python版本的scripts中,所以选择这个版本python的python.exe. 然后要点击apply后再点击ok
- 此时我们从LXML中导入etree不报错就说明安装成功了
'''导包(start)'''
from lxml import etree
'''导包(end)'''
3. 常用语法
为了方便讲解,我们先不爬取数据,而是用一个本地的html文件来讲解知识点,后期我们都是直接解析爬取下来的数据的,基本不会放到本地文件中然后再解析
- 解析本地文件
html_tree = etree.parse('xx.html')
- 解析服务器响应文件
html_tree = etree.HTML(response.read().decode('utf-8'))
- 解析xpath,这就是我们前面安装xpath插件的原因,有了它,就可以获得xpath路径,有了路径,可以直接定位我们需要的内容
html_tree.xpath(xpath路径)
- 路径查询
'''
1. //目标:查找目标所有子孙结点,不考虑层级关系
2. /目标:查找目标直接子节点
'''
- 谓词查询
'''
1. 目标[@指定属性]:查找目标中,有指定属性的,例如/li[@id]就是li标签有id属性的
2. 目标[@id="maincontent"]:查找目标中,有指定属性,属性值是maincontent的
'''
- 属性查询
'''
1. //@属性名:查询目标指定属性的属性值,例如/@class就是查询目标class属性的属性值
'''
- 模糊查询
'''
1. 目标[contains(@属性,"he")]:查询目标标签中的指定属性的属性值包含he的目标
2. 目标[starts-with(@属性,"he")]:查询目标中指定属性以he开头的目标
'''
- 内容查询
'''
1. 目标/text():查询目标的内容
'''
- 逻辑查询
'''
1. 目标[@id="head" and @class = "s_down"]: 查询目标,属性既要满足@id="head"又要满足@class = "s_down"
2. 目标1 | 目标2: 和上面不同,这个针对标签,查询目标1或目标2
'''
4. 语法基本使用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
</style>
</head>
<body>
<ul>
<li>北京</li>
<li>上海</li>
<li>广州</li>
<li>深圳</li>
</ul>
<ul>
<li>西安</li>
<li>成都</li>
<li>大连</li>
<li>武汉</li>
</ul>
</body>
</html>
- lxml严格遵守html规范,如果一个标签只有起始没有终止标签的话,就会提示,例如我们刚才给出的html文件中,meta标签没有终止
- 将结束标签补上即可,再次运行就会正确的获取解析对象
3. 获取body下所有子孙中的ul标签,以及所有子孙中的li标签 |
---|
- 获取ul
'''导包(start)'''
from lxml import etree
'''导包(end)'''
html_tree = etree.parse('new_file.html')
ul_list = html_tree.xpath("//body/ul")
print(ul_list)
- 获取li
'''导包(start)'''
from lxml import etree
'''导包(end)'''
html_tree = etree.parse('new_file.html')
ul_list = html_tree.xpath("//body//li")
print(ul_list)
print(len(ul_list))
4. 找到body下所有ul标签中有id属性并且为1的,然后在这个ul标签下找所有li标签中有id的,并输出标签内容 |
---|
其中/text()表示输出目标标签内容
'''导包(start)'''
from lxml import etree
'''导包(end)'''
html_tree = etree.parse('new_file.html')
ul_list = html_tree.xpath("//body/ul[@id='1']/li[@id]")
print(ul_list)
print(len(ul_list))
5. 查询id=1的ul下id为1.1的li,获取这个li的style属性的属性值 |
---|
'''导包(start)'''
from lxml import etree
'''导包(end)'''
html_tree = etree.parse('new_file.html')
ul_list = html_tree.xpath("//ul/li[@id='1.1']/@style")
print(ul_list)
print(len(ul_list))
6. 模糊查询:li标签中,id属性值包含1的,和li标签中,id属性值以2开头的 |
---|
'''导包(start)'''
from lxml import etree
'''导包(end)'''
html_tree = etree.parse('new_file.html')
ul_list = html_tree.xpath("//ul/li[contains(@id,'1')]/@id")
print("li标签中,id属性值包含1的有:",ul_list)
ul_list = html_tree.xpath("//ul/li[starts-with(@id,'2')]/@id")
print("li标签中,id属性值以2开头的有:",ul_list)
5. 爬取百度首页指定内容
- 打开百度首页,按下f12,并且打开xpath插件。我们如何知道我们写的xpath路径对不对呢
- 只需要在xpath插件的QUERY窗口编写代码,它会不断动态展示我们定位到了什么内容
- 当我们成功定位到百度一下四个字后,就可以将这段路径复制下来了
'''导包(start)'''
from lxml import etree
import urllib.request
import urllib.parse
'''导包(end)'''
url = "https://www.baidu.com/"
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode("utf8")
tree = etree.HTML(content)
result = tree.xpath('//input[@id="su"]/@value')
print(result)
6. 批量爬取图片
- 爬取站长素材网站,高清图片中的前10页数据中的图片,直接将图片保存到本地
- 我们发现网页源码中,图片都在class="item"的div中的img标签中。图片url在data-original属性中,图片名字在alt属性中
注意,网站采用懒加载,一定要提取正确的图片url,每个网站懒加载形式不一样,这个网站将真正url放在了data-original属性,而其它网站也要具体情况具体分析
- 下图我们发现,第一页url为https://sc.chinaz.com/tupian/
index
.html。而第二页为https://sc.chinaz.com/tupian/index_2
.html,而这就是url的规律,除了第一页以外,其它页就是index_页码而已
- 获取每个图片的名字alt://div[starts-with(@class,“item”)]/img/@alt
- 获取每个图片的地址data-original://div[starts-with(@class,“item”)]/img/@data-original
'''导包(start)'''
from lxml import etree
import urllib.request
import urllib.parse
import os
'''导包(end)'''
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"}
def get_url_byPage(page):
if page == 1:
url = "https://sc.chinaz.com/tupian/index.html"
else:
url = "https://sc.chinaz.com/tupian/index_"+str(page)+".html"
return url
def get_content(url):
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode("utf8")
return content
def get_img_alt(content):
tree = etree.HTML(content)
result = tree.xpath('//div[starts-with(@class,"item")]/img/@alt')
return result
def get_img_url(content):
tree = etree.HTML(content)
result = tree.xpath('//div[starts-with(@class,"item")]/img/@data-original')
return result
def download_img(alt_list,img_url_list,page):
flg = os.path.exists("./img/第" + str(page) + "页/")
if flg == False:
os.mkdir("./img/第" + str(page) + "页/")
for i in range(len(alt_list)):
urllib.request.urlretrieve(url = "https:"+img_url_list[i],filename="./img/第"+str(page)+"页/"+alt_list[i]+".jpg")
if __name__ == '__main__':
start_page = 1
end_page = 10
for page in range(start_page, end_page+1):
url = get_url_byPage(page = page)
content = get_content(url=url)
alt_list = get_img_alt(content=content)
img_url_list = get_img_url(content=content)
download_img(alt_list=alt_list,img_url_list=img_url_list,page=page)
二、JsonPath
1. 安装和基本使用
jsonpath主要是用于解析json数据的,例如ajax数据。与XPath是不同的,它定位的是html页面内容,而jsonPath是json数据的内容,但是我们可以对比两者的语法进行学习
依然使用pip进行安装即可,pip install jsonpath
JSONPath与XPath的语法对比
XPath | JSONPath | Description |
---|
/ | $ | 根元素 |
. | @ | 当前元素 |
/ | .or[] | 子元素 |
… | 无 | 父元素 |
// | … | 递归下降 |
* | * | 通配符,表示所有元素 |
@ | 无 | 属性访问字符 |
[] | [] | 子元素操作符 |
| | [,] | 连接操作符在XPath结果合并其它结点集合。JSONPath允许name或数组索引 |
无 | [start: end:step] | 数组分割操作从ES4借鉴 |
[] | ?() | 应用过滤表示式 |
无 | () | 脚本表达式,使用在脚本引擎下 |
() | 无 | Xpath分组 |
{ "store": {
"book": [
{ "category": "修真",
"author": "六道",
"title": "坏蛋是怎样练成的",
"price": 8.95
},
{ "category": "修改",
"author": "天蚕土豆",
"title": "斗破苍穹",
"price": 12.99
},
{ "category": "修真",
"author": "唐家三少",
"title": "斗罗大陆",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "修真",
"author": "南派三叔",
"title": "星辰变",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "黑色",
"price": 19.95
}
}
}
'''导包(start)'''
import json
import jsonpath
'''导包(end)'''
obj = json.load(open(file='store.json',mode='r',encoding='utf-8'))
print("书店所有书的作者(XPath:/store/book/author):",jsonpath.jsonpath(obj=obj,expr='$.store.book[*].author'))
print("所有的作者(XPath://author):",jsonpath.jsonpath(obj=obj,expr='$..author'))
print("所有元素(XPath:/store/*):",jsonpath.jsonpath(obj=obj,expr='$.store.*'))
print("所有的price(XPath:/store//price):",jsonpath.jsonpath(obj=obj,expr='$.store..price'))
print("第三本书(XPath:/store/book[3]):",jsonpath.jsonpath(obj=obj,expr='$.store.book[2]'))
print("最后一本书(XPath://book[last()]):",jsonpath.jsonpath(obj=obj,expr='$.store.book[(@.length-1])'))
print("前两本书(XPath://book[position()<3]):",jsonpath.jsonpath(obj=obj,expr='$..book[0,1]'))
print("前两本书(XPath://book[position()<3]):",jsonpath.jsonpath(obj=obj,expr='$..book[:2]'))
print("所有包含isbn的书(XPath://book[isbn]):",jsonpath.jsonpath(obj=obj,expr='$..book[?(@.isbn)]'))
print("价格低于10的书(XPath://book[price<3]):",jsonpath.jsonpath(obj=obj,expr='$..book[?(@.price<10)]'))
print("所有元素(XPath://*):",jsonpath.jsonpath(obj=obj,expr='$..*'))
2. 解析淘票票网站
需要注意的是,淘票票网站返回的是json数据是 jsonp108(真正的json数据),所以要去掉左边的"jsonp108(“和右边的”)"
'''导包(start)'''
import json
import jsonpath
import urllib.request
import urllib.parse
'''导包(end)'''
headers = {
'Host':'dianying.taobao.com',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0',
'Accept':'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01',
'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'X-Requested-With':'XMLHttpRequest',
'bx-v':'2.5.14',
'Connection':'keep-alive',
'Referer':'https://dianying.taobao.com/?spm=a1z21.3046609.city.12.32c0112au7IHxT&city=513200',
'Cookie':'thw=cn; tfstk=fkuX8Ein_tXffnEWUmdzRpGnUqzZfxTUH1NttfQV6rUYB1M-65QOklLJyWVUMlewmAZsQYqturlqFRHmERyY_m71B0hiQ-zZ61NtUrEi0Ry4CPGidm9e8elmiPq9Lp8U3t9sZP297P3bgcvQWpJezelmiP4tbJwWAr0STwAJ211mae75ygt5cjV1cxHjPSZ84jeYdJRaGotri8_TZ4NfkJhYFNQanKT2hbG2hwczoqkQTx8RlvGnrYN-pF97SYmtB5MJ-OzmYx03Vx-dyXU_hAEsqp_UGro7AqiDsThxL-iLof5B3bZTOq4q4K_LpDGxY70wEZes2c34mPXvLoctVYNbJg718J1TOG17xNF5mQO5jG07Ey4HwvEU6oFuGzOWNtaRKNQNqVR5XirYZSNHNQ6b0; isg=BEdHrg3y7XiTzGnCmulFDs3m1fsRTBsuIDClgxk0HVb9iGdKIR6Nfs5OKsjWe_Om; cna=coEXH+W0EmwCAWootOOiWFwe; t=66d61130e546591651239d33134f2cd6; cookie2=1ad21682e00857c47274c2a8240ea3cc; v=0; _tb_token_=e155e9eeba938; xlly_s=1; tb_city=513200; tb_cityName="sKKw0w=="',
'Sec-Fetch-Dest':'empty',
'Sec-Fetch-Mode':'cors',
'Sec-Fetch-Site':'same-origin,'
}
url = 'https://dianying.taobao.com/cityAction.json?activityId=&_ksTS=1722218140233_107&jsoncallback=jsonp108&action=cityAction&n_s=new&event_submit_doGetAllRegion=true'
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print("返回jsonP数据,无法直接用json解析,需要将左右圆括号切割:",content)
one_split = content.split('(')
print("将左圆括号切割:",one_split[1])
two_split = one_split[1].split(')')
print("将右圆括号切割:",two_split[0])
with open(file="淘票票.json",mode='w',encoding='utf-8') as f:
f.write(two_split[0])
json_data = json.load(open(file='淘票票.json',mode='r',encoding='utf-8'))
print("解析出所有城市名字:",jsonpath.jsonpath(json_data,'$..regionName'))
三、BeautifulSoup
- 简称BS4,和lxml一样是一个html解析器,主要功能也是解析和提取html数据
- 缺点是没有lxml效率高,但是优点是接口设计人性化,使用起来比较方便
1. 安装和基本使用
- 使用命令pip install bs4进行安装
- 代码中导入不报错,确保安装成功
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
</style>
</head>
<body>
<ul id="1">
<a href="www.baidu.com"></a>
<li id="1.1" class="classStyle1" style="background-color: aliceblue">北京</li>
<li id="1.2">上海</li>
<li id="2.1">
广州<a href="www.guangzhou.com"></a>
<span></span>
</li>
<li id="2.2">深圳</li>
</ul>
<ul>
<li id = "6.1" title="西安">西安</li>
<li class="chengdu">成都</li>
<li class="classStyle1">大连</li>
<li id = "wuhan" title="武汉">武汉</li>
</ul>
</body>
</html>
- 要重点掌握select选择器方法,这个和css选择器的代码完全一样,写过css样式的人都知道,它可以很方便的选择目标元素应用样式
- 而其他的find和find_all了解即可
'''导包(start)'''
from bs4 import BeautifulSoup
'''导包(end)'''
soup = BeautifulSoup(open('new_file.html', encoding='utf-8'), 'lxml')
print("返回文档第一个li标签",soup.find(name='li'))
print("返回文档第一个title='西安'的li标签的属性",soup.find(name='li',title="西安").attrs)
print("返回文档第一个class='chengdu'的li标签",soup.find(name='li',class_="chengdu"))
print("==========================================================================================================================================")
print("返回所有的li标签",soup.find_all(name='li'))
print("返回所有的li标签和a标签中的前3个标签",soup.find_all(name=['a','li'],limit=3))
print("=================================================select选择器,重点需要掌握的方法==============================================================")
print("标签选择器,选择li",soup.select('li'))
print("标签选择器,选择span和a",soup.select('span,a'))
print("类选择器选择指定class,点开头就是类名。选择类名为classStyle1的",soup.select('.classStyle1'))
print("id选择器选择指定id,#开头就是id。选择id为wuhan的",soup.select('#wuhan'))
print("属性选择器选择li标签包含title属性的",soup.select('li[title]'))
print("属性选择器选择li标签包含title属性,并指定title='武汉'",soup.select('li[title="武汉"]'))
print("层级选择器选择ul标签下所有的子孙结点a",soup.select('ul a'))
print("层级选择器选择ul标签下\"直接子结点\"a",soup.select('ul>a'))
'''导包(start)'''
from bs4 import BeautifulSoup
'''导包(end)'''
soup = BeautifulSoup(open('new_file.html', encoding='utf-8'), 'lxml')
data = soup.select(selector='#wuhan')
print("通过节点对象obj.name获取标签名:",data[0].name)
print("通过节点对象obj.attrs获取节点属性字典:",data[0].attrs)
print("通过节点对象obj.attrs.get('属性名')获取节点属性字典中的id属性的属性值:",data[0].attrs.get('id'))
print("通过节点对象obj.get_text()获取标签内容:",data[0].get_text())
2. 爬取百度首页指定内容
前面使用xpath完成了爬取,而bs4和xpath是一样的,只是bs4更加方便一点,代码逻辑是完全一样的,只是将xpath的语法换成bs4而已
'''导包(start)'''
import urllib.request
import urllib.parse
from bs4 import BeautifulSoup
'''导包(end)'''
url = "https://www.baidu.com/"
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode("utf8")
soup = BeautifulSoup(content, 'lxml')
data = soup.select(selector='input[id="su"]')[0]
print("获取到的内容:",data)
print("提取其value属性的值:",data.attrs.get('value'))