解析库
解析库的意思是对我们需要爬取的数据进行操作,从而获得想要的数据。
解析库有很多种,我们可以根据自己的习惯进行选取。
主要分为以下四种:
- 正则表达式
- xpath
- Beautiful Soup
- pyquery
一、正则表达式
首先讲解正则表达式的使用方法.
1) 元字符
元字符 | 匹配内容 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配所有普通字符(数字、字母或下划线) |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始位置 |
$ | 匹配字符串的结尾位置 |
\W | 匹配非字母或数字或下划线 |
\D | 匹配非数字 |
\S | 匹配非空白符 |
a|b | 匹配字符 a 或字符 b |
() | 正则表达式分组所用符号,匹配括号内的表达式,表示一个组。 |
[...] | 匹配字符组中的字符 |
[^...] | 匹配除了字符组中字符的所有字符 |
2) 量词
量词 | 用法说明 |
---|---|
* | 重复零次或者更多次 |
+ | 重复一次或者更多次 |
? | 重复0次或者一次 |
重复n次 | |
重复n次或者更多次 | |
重复n到m次 |
3) 字符组
有时也会出现各种字符组成的字符组,这在正则表达式中使用[]
正则 | 待匹配字符 | 匹配结果 | 说明 |
---|---|---|---|
[0123456789] | 8 | True | 在一个字符组里枚举所有字符,字符组里的任意一个字符 和"待匹配字符"相同都视为可以匹配。 |
[0123456789] | a | False | 由于字符组中没有 "a" 字符,所以不能匹配。 |
[0-9] | 7 | True | 也可以用-表示范围,[0-9] 就和 [0123456789] 是一个意思。 |
[a-z] | s | True | 同样的如果要匹配所有的小写字母,直接用 [a-z] 就可以表示。 |
[A-Z] | B | True | [A-Z] 就表示所有的大写字母。 |
[0-9a-fA-F] | e | True | 可以匹配数字,大小写形式的 a~f,用来验证十六进制字符。 |
1、match方法
该方法用于指定文本模式和待匹配的字符串。第一个参数表示文本模式,第二个参数表示待匹配的字符串。
如果匹配成功,就可以调用group方法获取匹配成功的字符串。
import re
m = re.match('hello','hello')
if m is not None:
print(m.group()) #使用group方法
print(m.__class__.__name__) #输出m的类名
m = re.match('hello','hello world') #只要模式从字符串起始位置开始,也可以匹配成功
if m is not None:
print(m.group())
print(m)
hello
Match
hello
<re.Match object; span=(0, 5), match='hello'>
2、search方法
搜索是正则表达式的另一类常用的应用场景。也就是从一段文本找到一个或多个与文本模式相匹配的字符串。
import re
m = re.match('python','I love python')
print(m)
m = re.search('python','I love python')
print(m)
s = '.ind'
m = re.match(s,'bind')
if m is not None:
print(m.group())
print(m)
m = re.match(s,'bin')
print(m)
None
<re.Match object; span=(7, 13), match='python'>
bind
<re.Match object; span=(0, 4), match='bind'>
None
3、分组
如果一个模式字符串中有一对圆括号括起来的部分,那么这部分就会作为一组,可以通过group方法的参数获取指定的组匹配的字符串。在我们匹配一些需要抓取的信息时,往往会在一次模式匹配时,就获取很多想要的信息,那么此时,我们就需要将想要获取的信息用括号括起来,就可以形成一个列表,这个列表里面的内容就都是我们想要的内容。
import re
m = re.match('(\d{3})-(\d{4})-([a-z]{2})','123-4567-xy')
if m is not None:
print(m.group())
print(m.group(1))
print(m.group(2))
print(m.group(3))
print(m.groups())
print("========================")
123-4567-xy
123
4567
xy
('123', '4567', 'xy')
=======================
4、findall和finditer方法
findall函数用于查询字符串中某个正则表达式模式全部的非重复出现情况,finditer方法与findall方法相似,只不过使用finditer方法需要进行迭代,这样更节省内存。
import re
s = '12-a-abc54-a-xyz---78-A-ytr'
#r是规避转义的字符
result = re.findall(r'\d\d-a-[a-z]{3}',s)
#匹配出了两个与该模式字符串相匹配的字符串
print(result)
['12-a-abc', '54-a-xyz']
5、sub和subn方法
sub函数与subn函数用于实现搜索和替换功能。这两个函数功能几乎完全一样,只不过sub函数返回替换后的结果,subn函数返回一个元组,元组的第一个元素是替换后的结果,第二个元素是替换的总数。
import re
#使用sub函数,第一个参数是模式字符串,第二个参数是要替换的字符串,第三个参数是被替换的字符串
result = re.sub('Mike','Bill','Mike is my son,I like Mike')
print(result)
result = re.subn('Mike','Bill','Mike is my son,I like Mike')
print(result)
#运行结果
Bill is my son,I like Bill
#元组
('Bill is my son,I like Bill', 2)
6、split方法
split函数用于根据正则表达式分隔字符串,split函数的第一个参数是模式字符串,第二个参数是带分隔的字符串。
import re
result = re.split(';','Bill;Mike;John')
print(result)
#运行结果
['Bill', 'Mike', 'John']
实战案例:抓取猫眼电影TOP100榜单(http://www.maoyan.com/)
import requests
#导入正则表达式
import re
#导入会话,维持状态
from requests import Session
session = Session()
class CatEyeMovie:
def __init__(self):
"""
个人爬取网站的一些经验:
一开始我只使用了UA最为代理,后来发现爬取一段时间后,就不能在进行爬取了。
然后我就导入了Host和Referer两个请求头作为参数,然后又爬取一段时间后又不能
爬取了,然后我有采用了cookie值,并导入了session维持会话,后来保持稳定了,但
我也不能完全保证后面不会出现问题,还有一个需要大家注意的点,就是我们最好将UA
经常换一下,否则网站很容易判断你就是一个爬虫。
"""
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
'Host':'www.maoyan.com',
'Referer':'https: // www.maoyan.com / board / 6?timeStamp = 1705477546939 & channelId = 40011 & index = 1 & signKey = 53785d6920acbc1fad61a808070e6544 & sVersion = 1 & webdriver = false',
'Cookie':'__mta = 208959021.1705475089718.1705477531026.1705477547761.23;uuid_n_v = v1;uuid = AEE07180B50611EE8FD01FDE53D00FC1F4728774816242469EDC18C661A68E3E;_csrf = bd11e59bd2541a5db1defecd0b98453acc792bc7b43e8c627d9f291922d330a5;Hm_lvt_703e94591e87be68cc8da0da7cbd0be2 = 1705475088;_lx_utm = utm_source % 3DBaidu % 26utm_medium % 3Dorganic;_lxsdk_cuid = 18d163ca06e88 - 09e6b05cd12e1e - 26001951 - 1bcab9 - 18d163ca06fc8;WEBDFPID = z7vx1480u69z54z2y8wxxw25u87w9wy681w834vxv2w9795839872754 - 2020837336064 - 1705477335287GSOGGSIfd79fef3d01d5e9aadc18ccd4d0c95079584;token = AgFrJIp1VufADHtEUTi0mp6KitQUBV_Ejob2I8Ux0OAvh6hc4y2_WhnCuFWgooIo_KncuV_jDf4ttQAAAABjHQAAZVmX7tI2iKUXgdLslHWY_dwRCQzD5i7IK_Ayog8 - 7acQiKH6CO0VVeeAwm4LSd1d;uid = 4274654873;uid.sig = vNE7mg6CHWQbb4imGsjuWSza8rs;lxsdk = AEE07180B50611EE8FD01FDE53D00FC1F4728774816242469EDC18C661A68E3E;__mta = 208959021.1705475089718.1705477312976.1705477544324.6;Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2 = 1705477548;_lxsdk_s = 18d163ca06f - 4af - c1e - 68b % 7C % 7C62'
}
def get_html(self):
#通过分析网站的URL,我们可以得出网站地址的规律
for i in range(0,101,10):
url = f'https://www.maoyan.com/board/4?timeStamp=1705475173402&channelId=40011&index=10&signKey=31588cad6150fc9509dd3f73c509fdac&sVersion=1&webdriver=false&offset={i}'
response = session.get(url,headers=self.headers)
#作判断,如果不是200,表示爬取不了
if response.status_code == 200:
html = response.text
#print(html)
self.re_html(html)
def re_html(self,html):
#使用正则表达式获取电影名和主演
re_act = '<p\sclass="name">.*?>(.*?)</a></p>\n.*?<p\s.*?>\n(.*?)\n.*?</p>'
#通过正则表达式获取图片链接
re_img_src = '<img\sdata-src="(.*?)".*?/>'
lists = re.findall(re_act,html,re.S)
img_lists = re.findall(re_img_src,html,re.S)
for list,img_list in zip(lists,img_lists):
title = list[0]
#使用strip函数去掉字符串两头的空格
lead_act = list[1].strip()
print(title)
print(lead_act)
print(img_list)
print("==================")
if __name__ == '__main__':
cat = CatEyeMovie()
cat.get_html()
我不是药神
主演:徐峥,周一围,王传君
https://p0.pipi.cn/mmdb/d2dad59253751bd236338fa5bd5a27c710413.jpg?imageView2/1/w/160/h/220
==================
肖申克的救赎
主演:蒂姆·罗宾斯,摩根·弗里曼,鲍勃·冈顿
https://p0.pipi.cn/mmdb/fb7386020fa51b0fafcf3e2e3a0bbe694d17d.jpg?imageView2/1/w/160/h/220
==================
海上钢琴师
主演:蒂姆·罗斯,比尔·努恩 ,克兰伦斯·威廉姆斯三世
https://p0.pipi.cn/mmdb/d2dad592c7e7e1d2365bf1b63cd25951b722b.jpg?imageView2/1/w/160/h/220
==================
绿皮书
主演:维果·莫腾森,马赫沙拉·阿里,琳达·卡德里尼
https://p0.pipi.cn/mmdb/d2dad59253751b230f21f0818a5bfd4d8679c.jpg?imageView2/1/w/160/h/220
==================
霸王别姬
主演:张国荣,张丰毅,巩俐
https://p0.pipi.cn/mmdb/fb7386beddd338537c8ea3bb80d25a9078b13.jpg?imageView2/1/w/160/h/220
==================
美丽人生
主演:罗伯托·贝尼尼,朱斯蒂诺·杜拉诺,赛尔乔·比尼·布斯特里克
https://p0.pipi.cn/mmdb/d2dad592c7e7e1d2367a3507befaed31a5903.jpg?imageView2/1/w/160/h/220
==================
这个杀手不太冷
主演:让·雷诺,加里·奥德曼,娜塔莉·波特曼
https://p0.pipi.cn/mmdb/d2dad592c7e7e13ba3ddd25677b4d70fc45fa.jpg?imageView2/1/w/160/h/220
==================
小偷家族
主演:中川雅也,安藤樱,松冈茉优
https://p0.pipi.cn/mmdb/d2dad5925372ffd7c387a9d01bddad81625c3.jpg?imageView2/1/w/160/h/220
==================
星际穿越
主演:马修·麦康纳,安妮·海瑟薇,杰西卡·查斯坦
https://p0.pipi.cn/mmdb/d2dad592b125bfc9fd300b8a46169f8008efb.jpg?imageView2/1/w/160/h/220
==================
怦然心动
主演:玛德琳·卡罗尔,卡兰·麦克奥利菲,艾丹·奎因
https://p0.pipi.cn/mmdb/d2dad592b122ff8d3387a93ccab6036f616c1.jpg?imageView2/1/w/160/h/220
==================
盗梦空间
主演:莱昂纳多·迪卡普里奥,渡边谦,约瑟夫·高登-莱维特
https://p0.pipi.cn/mmdb/fb7386510fa2ffd7c3e19bcb2e6ea4dc44460.jpg?imageView2/1/w/160/h/220
==================
泰坦尼克号
主演:莱昂纳多·迪卡普里奥,凯特·温丝莱特,比利·赞恩
https://p0.pipi.cn/mmdb/fb73869af2a51b339e537c802afb40db7a7fe.jpg?imageView2/1/w/160/h/220
==================
哪吒之魔童降世
主演:吕艳婷,囧森瑟夫,瀚墨
https://p0.pipi.cn/mmdb/d2dad592537923f0ee07acada3ac59b9f3ffb.jpg?imageView2/1/w/160/h/220
==================
二、lxml和xpath
1、lxml基础
lxml是python的一个解析库,用于解析HTML和XML,支持xpath解析方式。由于lxml底层是使用C语言编写的,所以解析效率非常高。但是lxml插件需要安转,在这里,我就不讲如何安装,大家可以用自己的方法去安装。
2、操作xml
lxml可以读取XML文件,也可以使用字符串形式的XML文档。读取XML文件时,需要使用parse函数,该函数需要传入XML文件名。如果要解析字符串形式的XML文档,需要使用fromstring函数,该函数的参数就是XML字符串。
<products>
<product id = "0001">
<name>手机</name>
<price>2000</price>
</product>
<product id = "0002">
<name>电脑</name>
<price>8350</price>
</product>
</products>
from lxml import etree
tree = etree.parse('product.xml')
print(type(tree))
#将tree重新转换为字符串形式的XML文档
print(str(etree.tostring(tree,encoding="utf-8"),'utf-8'))
#获得根结点对象
root = tree.getroot()
#输出根结点的名称
print(root.tag)
#获得根结点的所有子节点
children = root.getchildren()
for child in children:
#获取子节点的ID属性值
print(child.get('id'))
#获取子节点的文本内容
print(child[0].text)
print(child[1].text)
#重写XML文档
root = etree.fromstring(
'''
<product>
<product1 name="iphone"/>
<product2 name="iPad"/>
</product>
'''
)
#注意:从这里root的值就开始改变了,不在是XML文件里的值,而是自己写的
print(root.tag)
children = root.getchildren()
for child in children:
print(child.get('name'))
<class 'lxml.etree._ElementTree'>
<products>
<product id="0001">
<name>手机</name>
<price>2000</price>
</product>
<product id="0002">
<name>电脑</name>
<price>8350</price>
</product>
</products>
products
0001
手机
2000
0002
电脑
8350
product
iphone
iPad
3、操作HTML
使用lxml库解析HTML与解析XML类似,在这里,我通过代码能够让读者更加容易理解。
from lxml import etree
#创建lxml.etree.HTMLPareser对象
parser = etree.HTMLParser()
print(type(parser))
#读取并解析html文件
tree = etree.parse('product.html',parser)
#获取根结点
root = tree.getroot()
#将html文档转换为可读格式
result = etree.tostring(root,encoding='utf-8',pretty_print=True,method="html")
print(result,'utf-8')
#获取根结点名称
print(root.tag)
print(root.get('lang'))
#在这里相当于列表,head是列表里的第一个值,body是列表的第二个值
print(root[0][0].get('charset'))
print(root[0][1].text)
print(root[1][0].text)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是一个HTML文件</title>
</head>
<body>
<h1>解析库</h1>
</body>
</html>
<class 'lxml.etree.HTMLParser'>
b'<html lang="en">\r\n<head>\r\n <meta charset="UTF-8">\r\n <title>\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe4\xb8\xaaHTML\xe6\x96\x87\xe4\xbb\xb6</title>\r\n</head>\r\n<body>\r\n <h1>\xe8\xa7\xa3\xe6\x9e\x90\xe5\xba\x93</h1>\r\n</body>\r\n</html>\n' utf-8
html
en
UTF-8
这是一个HTML文件
解析库
4、Xpath
1) 基本语法使用
Xpath 使用路径表达式在文档中选取节点,下表列出了常用的表达式规则:
表达式 | 描述 |
---|---|
node_name | 选取此节点的所有子节点。 |
/ | 绝对路径匹配,从根节点选取。 |
// | 相对路径匹配,从所有节点中查找当前选择的节点,包括子节点和后代节点,其第一个 / 表示根节点。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性值,通过属性值选取数据。常用元素属性有 @id 、@name、@type、@class、@tittle、@href。 |
2) xpath通配符
Xpath 表达式的通配符可以用来选取未知的节点元素,基本语法如下:
通配符 | 描述说明 |
---|---|
* | 匹配任意元素节点 |
@* | 匹配任意属性节点 |
node() | 匹配任意类型的节点 |
xpath其它知识点
1、可以使用“|”,表示多路径匹配,有时获取相同的位置格式,解析语法并不是完全一样的。
2、xpath还有很多内建函数,在这里我说一个经常用的就是“text属性”,后面进行实战案例时,会给大家详细介绍。
想要了解更多关于 Xpath 的知识可访问官方网站:https://www.w3.org/TR/xpath/
实战案例:抓取起点中文网小说信息()
#导入Excel表格
import xlwt
import requests
#导入随机函数,可以使用随机UA代理池
import random
from lxml import etree
#这里面是一些UA代理,可以随机切换UA,避免浏览器发现爬虫程序
from UA_info import ua_list
class StartSpider:
#定义表头
header = ['书名', '作者', '类型', '完成度', '介绍']
#创建workbook对象
book = xlwt.Workbook(encoding='utf-8')
#添加一个名为novels的sheet
sheet = book.add_sheet('novels')
#为Excel表单添加表头
for h in range(len(header)):
sheet.write(0, h, header[h])
def __init__(self):
#计数器
self.i = 1
self.headers = {
'User-Agent': random.choice(ua_list),
}
def get_html(self):
for i in range(1,6):
url = f'https://www.qidian.com/all/page{i}/'
response = requests.get(url,headers=self.headers)
if response.status_code == 200:
html = response.text
self.xml_html(html)
def xml_html(self,html):
xml = etree.HTML(html)
#书名的解析语法
title_list = xml.xpath('//*[@id="book-img-text"]/ul/li/div[2]/h2/a/text()')
#作者名的解析语法
author_list = xml.xpath('//*[@id="book-img-text"]/ul/li/div[2]/p[1]/a[1]/text()')
#小说类型的解析语法
type_list1 = xml.xpath('//ul[@class="all-img-list cf"]//p/a[2]/text()')
type_list2 = xml.xpath('//ul[@class="all-img-list cf"]//p/a[3]/text()')
#小说完成度解析语法
complete_list = xml.xpath('///ul[@class="all-img-list cf"]//p[@class="author"]/span/text()')
#小说简介
introduce_list = xml.xpath('//ul[@class="all-img-list cf"]//p[@class="intro"]/text()')
for title,author,type1,type2,complete,introduce in zip(title_list,author_list,type_list1,type_list2,complete_list,introduce_list):
type = type1+'·'+type2
print(title)
print(author)
print("========================")
self.save_data(title,author,type,complete,introduce)
#定义保存数据的函数
def save_data(self,title,author,type,complete,introduce):
self.sheet.write(self.i,0,title)
self.sheet.write(self.i,1,author)
self.sheet.write(self.i,2,type)
self.sheet.write(self.i,3,complete)
self.sheet.write(self.i,4,introduce)
self.i += 1
if __name__ == '__main__':
start = StartSpider()
start.get_html()
#将内存中的Excel数据保存为该文件
StartSpider.book.save('novels.xls')
灵境行者
卖报小郎君
========================
赤心巡天
情何以甚
========================
宿命之环
爱潜水的乌贼
========================
都重生了谁谈恋爱啊
错哪儿了
========================
深海余烬
远瞳
========================
谁让他修仙的!
最白的乌鸦
========================
这游戏也太真实了
晨星LL
========================
万古神帝
飞天鱼
========================
逼我重生是吧
幼儿园一把手
========================
玄鉴仙族
季越人
========================
以上是写入Excel表格中的数据!
标签:匹配,self,list,re,html,print,解析 From: https://www.cnblogs.com/wjx-2005-07-01/p/17970719