首页 > Python基础教程 >
-
爬虫之数据解析
数据解析
数据解析:解析或提取数据,从通用爬虫获取的整张页面中,取得指定的局部数据
- 作用:实现聚焦爬虫
-
实现方式:
- 正则(相比来说麻烦一些)
- bs4(python中独有的)
- Xpath(java,php,python均可使用)
- pyquery(python独有)
-
数据解析的通用原理是什么?
-
解析的一定是html页面的源码数据
- 解析标签中存储的文本内容
- 解析标签属性的属性值
-
原理:
- 1,标签定位
- 2,取文本或者取属性
-
解析的一定是html页面的源码数据
-
爬虫实现的流程:
- 指定url
- 发请求
- 获取响应数据
- 数据解析
- 将解析到的数据持久化存储
正则解析
正则回顾
单字符:
. : 除换行以外所有字符
[] :[aoe] [a-w] 匹配集合中任意一个字符
\d :数字 [0-9]
\D : 非数字
\w :数字、字母、下划线、中文
\W : 非\w
\s :所有的空白字符包,括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S : 非空白
数量修饰:
* : 任意多次 >=0
+ : 至少1次 >=1
? : 可有可无 0次或者1次
{m} :固定m次 hello{3,}
{m,} :至少m次
{m,n} :m-n次
边界:
$ : 以某某结尾
^ : 以某某开头
分组:
(ab)
贪婪模式: .*
非贪婪(惰性)模式: .*?
re.I : 忽略大小写
re.M :多行匹配
re.S :单行匹配
re.sub(正则表达式, 替换内容, 字符串)
- 需求:使用正则将http://duanziwang.com/category/%E6%90%9E%E7%AC%91%E5%9B%BE/对应的段子标题取出
import requests
import re
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
}
url = 'http://duanziwang.com/category/搞笑图/'
#捕获到的是字符串形式的响应数据
page_text = requests.get(url=url,headers=headers).text
#数据解析
ex = '<div class="post-head">.*?<a href="http://duanziwang.com/\d+\.html">(.*?)</a></h1>'
ret = re.findall(ex,page_text,re.S)#爬虫使用正则做解析的话re.S必须要使用
#持久化存储
with open("./title.txt","a",encoding="utf-8") as f:
for i in ret:
f.write(f"{i}\n")
bs4解析
-
环境安装
- pip install bs4
- pip install lxml
-
解析原理&实现流程
- 1,实例化一个BeautifulSoup的对象,需要将等待被解析的页面源码数据加载到该对象中
- 2,调用BeautifulSoup对象中相关的属性和方法进行标签定位和文本数据的t提取
-
BeautifulSoup如何实例化
- 方式1:BeautifulSoup(等待被解析的源码数据,“lxml”),将本地存储的一张html文件中的指定数据进行解析
- 方式2:BeautifulSoup(响应数据,"lxml"),将从互联网中爬取的数据进行数据解析
常用的方法和属性
-
标签定位:根据标签名进行定位,只返回第一个出现的标签
-
soup.标签名 返回当前源码中的第一个出现的标签名
-
属性定位:根据指定的属性进行对应标签的定位
-
soup.find(标签名,标签属性=属性值) 只有class属性加 class_
soup,find("tagName")
-
soup.findall(标签名,标签属性=属性值)
-
-
选择器定位
-
soup.select(".类")
soup.select("#id")
-
层级选择器
- 大于号:表示一个层级
- 空格:表示多个层级
-
-
-
取文本
-
标签定位到的标签.string
tag.string
- 取出标签下直系的文本内容
-
标签定位到的标签.text
tag.text
- 取出标签下所有的文本内容
-
-
取属性
-
标签定位到的标签["字符串形式的属性名称"]
tag.["attrName"]
-
案例:爬取小说
- 网址:https://www.52bqg.com/book_10508/
# 这种网站最好使用代理池
# 案例1
import requests
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
}
url = "https://www.52bqg.com/book_10508/"
fp = open("./九仙图.txt","w",encoding="utf-8")
page_text = requests.get(url=url,headers=headers)
page_text.encoding="GBK"
x = page_text.text
soup = BeautifulSoup(x,'lxml')
a_list = soup.select('#list a')
for i in a_list:
title = i.string
a_href = 'https://www.52bqg.com/book_10508/' + i['href']
page_text_a = requests.get(url=a_href,headers=headers)
page_text_a.encoding="GBK"
f = page_text_a.text
a_soup = BeautifulSoup(f,'lxml')
div_tag = a_soup.find('div',id='content')
content = div_tag.text
fp.write("\n" + title + "\n" + content + "\n")
print(title,"下载完成")
fp.close()
- 网址:http://www.balingtxt.com/txtml_84980.html
# 案例2
import requests
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
}
url = "http://www.balingtxt.com/txtml_84980.html"
page_text = requests.get(url=url,headers=headers)
page_text.encoding="utf-8"
page_text = page_text.text
menu_soup = BeautifulSoup(page_text,"lxml")
a_lst = menu_soup.select("#yulan > li > a")
fp = open("./天命相师.txt","w",encoding="utf-8")
for i in a_lst:
title = i.string
a_url = i["href"]
new_text = requests.get(url=a_url,headers=headers)
new_text.encoding="utf-8"
new_text = new_text.text
contcent_soup = BeautifulSoup(new_text,"lxml")
content = contcent_soup.find("div",class_="book_content").text
fp.write(f"{title}\n{content}\n")
print(f"{title} 下载完成!")
fp.close()
图片数据的爬取
基于requests
import requests
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
}
url = "http://a0.att.hudong.com/78/52/01200000123847134434529793168.jpg"
img_data = requests.get(url=url,headers=headers).content # 返回的是二进制类型的数据
with open("./tiger1.jpg","wb") as f:
f.write(img_data)
content 返回的是二进制数据,如爬取图片,音频,视频
基于urllib
from urllib import request
url = "http://a0.att.hudong.com/78/52/01200000123847134434529793168.jpg"
ret = request.urlretrieve(url=url,filename="./trger2.jpg")
print(ret)
- 区别:能否实现UA伪装
Xpath解析
-
环境安装
- pip install lxml
-
解析原理&流程
- 实例化一个etree的对象,且将被解析的页面源码数据加载到该对象中
- 使用etree对象中的xpath方法结合者不同形式的xpath表达式进行标签定位和数据的提取
-
实例化对象
- etree.parse("文件路径"):将本地存储的html文件中的数据加载到实例化好etree对象中
- etree.HTML("page_text"):将网络上爬取的数据加载到其中
-
XPath表达式
-
标签定位
-
最左侧的/:必须冲根节点定位标签(几乎不用)
-
非最左侧的/:表示一个层级
-
最左侧的//:可以从任意位置进行指定标签的定位
-
非最左侧的//:表示多个层级(最常用)
-
属性定位:"//tagName[@attrName='value']"
"//标签名[@属性名称=‘属性值’]"
- 索引定位:"//标签名[索引]" # 索引值从1开始,为了方便寻址
- //div[contains(@class,'ng')] # 所有的div中,class属性值中包含ng的
-
//div[start-with(@calss,'ta')] # 所有的div中,class属性值以'ta'开头的
-
-
取文本
- tree.xpath的返回值之列表,取值时需要索引
- /text():取直系的文本
- //text():取出所有的文本
-
取属性
- /@属性名称
-
案例:Xpath下载图片
-
需求:使用Xpath解析图片地址和名称且将图片进行下载保存到本地
- http://pic.netbian.com/4kfengjing/
- 爬取高清大图
import requests
import os
from lxml import etree
from urllib import request
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
}
dirName = "imglibs" # 存储图片的文件夹名字
if not os.path.exists(dirName):
os.mkdir(dirName) # 没有该文件夹就创建
page_url = "http://pic.netbian.com/4kfengjing/index_%d.html"
for page_num in range(1,10): # 全站数据爬取,根据自己情况选择爬取的页码数
if page_num == 1:
url = "http://pic.netbian.com/4kfengjing/"
else:
url = format(page_url%page_num)
page_text = requests.get(url=url,headers=headers).text
# 数据解析
tree = etree.HTML(page_text) # 实例化一个etree对象
img_lst = tree.xpath('//div[@class="slist"]/ul/li/a') # 返回一个a标签的列表
for i in img_lst:
img_href = "http://pic.netbian.com" + i.xpath("./@href")[0] # 路径拼接,找到大图页的地址
img_text = requests.get(url=img_href,headers=headers).text
new_tree = etree.HTML(img_text) # 实例化一个新的etree对象
img_list = new_tree.xpath('//a[@id="img"]/img')[0] # 找到图片的img标签对象
img_src = "http://pic.netbian.com" + img_list.xpath('./@src')[0] # 拼接高清大图的地址
img_alt = img_list.xpath('./@alt')[0].encode('iso-8859-1').decode('GBK') # 找到图片的名字
filepath = "./" + dirName + "/" + img_alt + ".jpg" # 加.jpg后缀
request.urlretrieve(img_src,filename=filepath) # 持久化存储
print(img_alt,"下载成功!")
print("Over!")
pyquery解析
-
安装
- pip install pyquery
-
引用
- from pyquery import PyQuery as pq
-
简介
- pyquery是jQuery的一个专供python使用的HTML解析的库,类似于bs4
使用方法
- 初始化方法
from pyquery import PyQuery as pq
doc =pq(html) #解析html字符串
doc =pq("http://news.baidu.com/") #解析网页
doc =pq("./a.html") #解析html 文本
- 基本CSS选择器
from pyquery import PyQuery as pq
html = '''
<div id="wrap">
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
print doc("#wrap .s_from link")
运行结果:
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
- 查找子元素
from pyquery import PyQuery as pq
html = '''
<div id="wrap">
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
#查找子元素
doc = pq(html)
items=doc("#wrap")
print(items)
print("类型为:%s"%type(items))
link = items.find('.s_from')
print(link)
link = items.children()
print(link)
运行结果:
<div id="wrap">
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
类型为:<class 'pyquery.pyquery.PyQuery'>
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
- 查找父元素
from pyquery import PyQuery as pq
html = '''
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
items=doc(".s_from")
print(items)
#查找父元素
parent_href=items.parent()
print(parent_href)
运行结果:
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link href="http://asda.com">asdadasdad12312</link>
<link href="http://asda1.com">asdadasdad12312</link>
<link href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
parent可以查找出外层标签包括的内容,与之类似的还有parents,可以获取所有外层节点。
- 查找兄弟元素
from pyquery import PyQuery as pq
html = '''
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link class='active1 a123' href="http://asda.com">asdadasdad12312</link>
<link class='active2' href="http://asda1.com">asdadasdad12312</link>
<link class='movie1' href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
items=doc("link.active1.a123")
print(items)
#查找兄弟元素
siblings_href=items.siblings()
print(siblings_href)
运行结果:
<link class="active1 a123" href="http://asda.com">asdadasdad12312</link>
<link class="active2" href="http://asda1.com">asdadasdad12312</link>
<link class="movie1" href="http://asda2.com">asdadasdad12312</link>
根据运行结果可以看出,siblings 返回了同级的其他标签
结论:子元素查找,父元素查找,兄弟元素查找,这些方法返回的结果类型都是pyquery类型,可以针对结果再次进行选择
- 遍历查找结果
from pyquery import PyQuery as pq
html = '''
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link class='active1 a123' href="http://asda.com">asdadasdad12312</link>
<link class='active2' href="http://asda1.com">asdadasdad12312</link>
<link class='movie1' href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
its=doc("link").items()
for it in its:
print(it)
运行结果:
<link class="active1 a123" href="http://asda.com">asdadasdad12312</link>
<link class="active2" href="http://asda1.com">asdadasdad12312</link>
<link class="movie1" href="http://asda2.com">asdadasdad12312</link>
- 获取属性信息
from pyquery import PyQuery as pq
html = '''
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link class='active1 a123' href="http://asda.com">asdadasdad12312</link>
<link class='active2' href="http://asda1.com">asdadasdad12312</link>
<link class='movie1' href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
its=doc("link").items()
for it in its:
print(it.attr('href'))
print(it.attr.href)
运行结果:
http://asda.com
http://asda.com
http://asda1.com
http://asda1.com
http://asda2.com
http://asda2.com
- 获取文本
from pyquery import PyQuery as pq
html = '''
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link class='active1 a123' href="http://asda.com">asdadasdad12312</link>
<link class='active2' href="http://asda1.com">asdadasdad12312</link>
<link class='movie1' href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
its=doc("link").items()
for it in its:
print(it.text())
运行结果:
asdadasdad12312
asdadasdad12312
asdadasdad12312
- 获取HTML信息
from pyquery import PyQuery as pq
html = '''
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link class='active1 a123' href="http://asda.com"><a>asdadasdad12312</a></link>
<link class='active2' href="http://asda1.com">asdadasdad12312</link>
<link class='movie1' href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
its=doc("link").items()
for it in its:
print(it.html())
运行结果:
<a>asdadasdad12312</a>
asdadasdad12312
asdadasdad12312
常用DOM操作
-
添加,移除class标签
addClass
removeClass
from pyquery import PyQuery as pq
html = '''
<div href="wrap">
hello nihao
<ul class="s_from">
asdasd
<link class='active1 a123' href="http://asda.com"><a>asdadasdad12312</a></link>
<link class='active2' href="http://asda1.com">asdadasdad12312</link>
<link class='movie1' href="http://asda2.com">asdadasdad12312</link>
</ul>
</div>
'''
doc = pq(html)
its=doc("link").items()
for it in its:
print("添加:%s"%it.addClass('active1'))
print("移除:%s"%it.removeClass('active1'))
运行结果:
添加:<link class="active1 a123" href="http://asda.com"><a>asdadasdad12312</a></link>
移除:<link class="a123" href="http://asda.com"><a>asdadasdad12312</a></link>
添加:<link class="active2 active1" href="http://asda1.com">asdadasdad12312</link>
移除:<link class="active2" href="http://asda1.com">asdadasdad12312</link>
添加:<link class="movie1 active1" href="http://asda2.com">asdadasdad12312</link>
移除:<link class="movie1" href="http://asda2.com">asdadasdad12312</link>
需要注意的是已经存在的class标签不会继续添加
- attr 为获取/修改属性 css 添加style属性