正则表达式解析
单字符:
. : 除换行以外所有字符 [] :[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(正则表达式, 替换内容, 字符串)
综合练习1 -- 图片下载
需求:将指定页的图片下载后保存到指定文件夹中
import urllib.request
import urllib.parse
import os
import re
s_pageNum=int(input('enter start page num:'))
e_pageNum=int(input('enter end page num:'))
#创建文件夹
if not os.path.exists('qiutu'):
os.mkdir('qiutu')
def get_request(url):
headers={
'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
return urllib.request.Request(url,headers=headers)
def analysis_contente(content):
url_list=[] #该列表是用来存储被拼接好的完整的图片的url
pl='<div class="thumb">.*?<img src="(.*?)" .*?>.*?</div>'
pa=re.compile(pl,re.S)
#该列表中存储就是当前页中所有的图片的url
imageUrl_list=pa.findall(content)
for n_url in imageUrl_list:
url='https:'+n_url
url_list.append(url)
return url_list
for page in range(s_pageNum,e_pageNum+1):
url='https://www.qiushibaike.com/pic/page/%s/?s=5116392'
url=format(url%str(page))
request=get_request(url)
response=urllib.request.urlopen(request)
content=response.read().decode()
#对请求到的网页源码进行指定内容的解析
image_url_list=analysis_contente(content)
for url in image_url_list:
request_image=get_request(url)
response_image=urllib.request.urlopen(request_image)
#获取图片原有的名称
image_name=url.split('/')[-1]
image_path='qiutu/'+image_name
#将图片写入本地进行存储
with open(image_path,'wb') as fp:
fp.write(response_image.read())
- 综合练习2 -- 抓取文章
#http://www.yikexun.cn/rensheng/ganwu/list_52_1.html
import re, urllib.request, urllib.parse, os
start = int(input('enter a start page:'))
end = int(input('enter a end page:'))
#新建文件夹
if not os.path.exists('一点点语录'):
os.mkdir('一点点语录')
#返回一个响应
def get_request(url):
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5792.400 QQBrowser/10.2.2101.400'}
resquest = urllib.request.Request(url,headers=headers)
response = urllib.request.urlopen(resquest)
return response
path = '一点点语录/人生感悟(升级版).txt'
with open(path,'w',encoding='utf-8') as f:
for page in range(start,end+1):
# url_list = []
url = 'http://www.yikexun.cn/rensheng/ganwu/list_52_'+str(page)+'.html'
webpage = get_request(url).read().decode()
url_list = re.findall(r'<div class="art-t">\s+<h3><a href="(.*?)">.*?</a></h3>',webpage)
print('开始第{}页'.format(page))
count = 1
for url in url_list:
pageInfo = get_request('http://www.yikexun.cn'+url).read().decode()
title = re.findall(r'<div class="arc-title">\s+<h1>(.*?)</h1>', pageInfo)
content = re.findall(r'<p>(.*?)</p>',pageInfo)
print('写入第{}篇文章标题'.format(count))
f.write(title[0]+'\n')
print('开始写入第{}篇文章'.format(count))
for paragraph in range(len(content) - 2):
if not 'img' in content[paragraph]:
if '<br/>' in content[paragraph] or ' ' in content[paragraph]:
if len(re.split('<br/>| ', content[paragraph])[0]) == 0:
f.write(re.split('<br/>| ', content[paragraph])[1]+'\n')
else:
f.write(re.split('<br/>| ', content[paragraph])[0]+'\n')
else:
f.write(content[paragraph]+'\n')
f.write('\n\n')
print('第{}篇文章写入完成'.format(count))
count += 1
print('第{}页结束'.format(page))
bs4:BeautifulSoup解析
- 在pycharm中,可以通过设置直接进行搜索安装 ,无需配置源
需要将pip源设置为国内源,阿里源、豆瓣源、网易源等
windows
(1)打开文件资源管理器(文件夹地址栏中) (2)地址栏上面输入 %appdata% (3)在这里面新建一个文件夹 pip (4)在pip文件夹里面新建一个文件叫做pip.ini ,内容写如下即可
[global]
timeout = 6000
index-url = https://mirrors.aliyun.com/pypi/simple/
trusted-host = mirrors.aliyun.com
linux
(1)cd ~
(2)mkdir ~/.pip
(3)vi ~/.pip/pip.conf (4)编辑内容,和windows一模一样
需要安装:pip install bs4
bs4在使用时候需要一个第三方库,把这个库也安装一下
pip install lxml
用法:
导入:
from bs4 import BeautifulSoup
使用方式:可以将一个html文档,转化为指定的对象,然后通过对象的方法或者属性去查找指定的内容
转化本地文件:
- soup = BeautifulSoup(open('本地文件'), 'lxml')
转化网络文件:
- soup = BeautifulSoup('字符串类型或者字节类型', 'lxml')
打印soup对象显示内容为html文件中的内容
- soup = BeautifulSoup('字符串类型或者字节类型', 'lxml')
根据标签名查找:
- soup.a 只能找到第一个符合要求的标签
获取属性:
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典
- soup.a.attrs['href'] 获取href属性
- soup.a['href'] 也可简写为这种形式
获取内容:
- soup.a.string
- soup.a.text
soup.a.get_text()
如果标签内还包含有子标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
find:查找
soup.find('a') 找到第一个符合要求的a
- soup.find('a', title="xxx")
- soup.find('a', alt="xxx")
- soup.find('a', class_="xxx")
- soup.find('a', id="xxx")
find方法不仅soup可以调用,普通的div对象也能调用,会去指定的div里面去查找符合要求的节点 div = soup.find('div', class_="tang")
print(div.find('a', class_="du"))
- find找到的都是第一个符合要求的标签
find_all
- soup.find_all('a')
- soup.find_all(['a', 'b']) 找到所有的a和b标签
- soup.find_all('a', limit=2) 限制前两个
select:
soup.select('#feng')
根据选择器选择指定的内容
常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
a, .dudu, #lala, .meme
div .dudu #lala .meme .xixi 下面好多级
div > p > a > .lala 只能是下面一级
select选择器返回永远是列表,需要通过下标提取指定的对象,然后获取属性和节点
该方法也可以通过普通对象调用,找到都是这个对象下面符合要求的所有节点
from bs4 import BeautifulSoup
#bs对象返回的是一组文件源码
soup = BeautifulSoup(open('soup_text.html',encoding='utf-8'), 'lxml')
soup
#soup.div
#soup.a.attrs['href']
#soup.a['href']
#soup.a.string
#soup.a.text
#soup.a.get_text()
#oup.find('a')
#soup.find('a',{'class':'du'})
#soup.find('a',class_="du")
#div_soup=soup.find('div',class_="song")
#len(soup.find_all('a',limit=2))
#soup.find_all(['a','div'])
#soup.select('#feng')
#soup.select('a')
#soup.select('.song > img')[0]['src']
XPath解析
概念:
xpath是一种路径表达式,用来在xml中查找指定的元素。
xml:
xml是用来存储和传输数据的一种格式。
xml和html的不同有两点:
- html用来显示数据,xml是用来传输数据
- html标签是固定的,xml标签是自定义的
xpath常用表达式:
常用的路径表达式
- // : 不考虑位置的查找
- / : 从跟节点开始往下查找
- @ : 选取属性
实例:
- /bookstore/book 选取根节点bookstore下面所有直接子节点book
- //book 选取所有book
- /bookstore//book 查找bookstore下面所有的book
- /bookstore/book[1] bookstore里面的第一个book
- /bookstore/book[last()] bookstore里面的最后一个book
- /bookstore/book[position()<3] 前两个book
- //title[@lang] 所有的带有lang属性的title节点
- //title[@lang='eng'] 所有的lang属性值为eng的title节点
属性定位:
- //li[@id="hua"]
- //div[@class="song"]
层级定位:(索引定位)
//div[@id="head"]/div/div[2]/a[@class="toindex"]
【注】索引从1开始
//div[@id="head"]//a[@class="toindex"]
【注】双斜杠代表下面所有的a节点,不管位置
逻辑运算:
- //input[@class="s_ipt" and @name="wd"]
模糊匹配:
contains
//input[contains(@class, "s_i")]
所有的input,有class属性,并且属性中带有s_i的节点
- //input[contains(text(), "爱")]
starts-with
- //input[starts-with(@class, "s")]
所有的input,有class属性,并且属性以s开头
- //input[starts-with(@class, "s")]
取文本:
- //div[@id="u1"]/a[5]/text() 获取节点内容
- //div[@id="u1"]//text() 获取节点里面不带标签的所有内容
取属性:
- //div[@id="u1"]/a[5]/@href
new_book.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>
from lxml import etree
tree=etree.parse('new_book.xml')
#xpath表达式需要载入xpath函数中进行执行
#xpath返回的是列表
#tree.xpath('/bookstore') #从根节点开始导航定位到的节点信息
#tree.xpath('/bookstore/book')
tree.xpath('//book[2]') #索引从1开始
tree.xpath('//book[last()]')
tree.xpath('//book[position()<2]')
tree.xpath('//title[@lang="eng"]')
代码中使用xpath
from lxml import etree
两种方式使用:将html文档变成一个对象,然后调用对象的方法去查找指定的节点(1)本地文件
tree = etree.parse(文件名)(2)网络文件
tree = etree.HTML(网页字符串)ret = tree.xpath(路径表达式)
【注】ret是一个列表
安装xpath插件:
可以执行xpath相应的路径指令
将xpath插件拖动到谷歌浏览器扩展程序(更多工具中)中,安装成功
启动和关闭插件 ctrl + shift + x- 案例:
from lxml import etree
url='http://www.haoduanzi.com/category-10_2.html'
#//div[@class="log cate10 auth1"]/h3/a/text() #取标题
#//div[@class="log cate10 auth1"]/div[@class="cont"]//text() #取内容
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
}
request=urllib.request.Request(url,headers=headers)
response=urllib.request.urlopen(request)
url_content=response.read().decode()
#使用xpath对url_conten进行解析
#使用xpath解析从网络上获取的数据
tree=etree.HTML(url_content)
#解析获取当页所有段子的标题
title_list=tree.xpath('//div[@class="log cate10 auth1"]/h3/a/text()')
text_list=tree.xpath('//div[@class="log cate10 auth1"]/div[@class="cont"]//text()')
list=str(text_list).split('阅读全文')
text=list[0:12]
JsonPath解析
- jsonpath: 用来解析json数据使用的
安装:
pip install lxml pip install jsonpath
jsonpath和xpath的对比
xPath jsonPath 描述 / $ 根元素 . @ 当前元素 / . or [] 子元素 .. n/a 父元素 // .. 递归下降,JSONPath是从E4X借鉴的。 * * 通配符,表示所有的元素 @ n/a 属性访问字符 [] [] 子元素操作符 \ [,] 连接操作符在XPath 结果合并其它结点集合。JSONP允许name或者数组索引。 n/a [start: end: step] 数组分割操作从ES4借鉴。 [] ?() 应用过滤表示式 n/a () 脚本表达式,使用在脚本引擎下面。 () n/a Xpath分组 注意:
xpath 索引下标从1开始 jsonpath 索引下标从0开始
处理json用到的函数
- 导包:import json
- json.dumps():将字典或者列表转化为json格式的字符串
- json.dump():将字典或者列表转化为json格式字符串并且写入到文件中
- json.loads():将json格式字符串转化为python对象(字典/列表)
- json.load():从文件中读取json格式字符串,转化为python对象
import json
json_obj={
"name":['张三','lisi','xiaohei'],
"age":[10,20,30]
}
#将json对象转换成json串
str_json=json.dumps(json_obj,ensure_ascii=False)
obj=json.loads(str_json)
#将json_obj转换成json格式的字符串,然后写入到磁盘
#json.dump(json_obj,open('./a.json','w'))
#将a.json文件中的json字符串转换成json对象
obj=json.load(open('./a.json','r'))
test.json
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
import jsonpath
import json
#jsonpath函数返回值是一个列表
#将test.json中的字符串提取出来
obj=json.load(open('./test.json','r'))
#使用jsonpath函数去执行jsonpath表达式
#获取书店里所有书的作者
#/store/book/author
#jsonpath.jsonpath(obj,'$.store.book[*].author')
#获取第三本书
#jsonpath.jsonpath(obj,'$.store.book[2]')[0]
#获取最后一本书
#jsonpath.jsonpath(obj,'$..book[(@.length-1)]')
#过滤出价格小于10的所有图书
#jsonpath.jsonpath(obj,'$..book[?(@.price<10)]')
#所有元素
jsonpath.jsonpath(obj,'$..*')
案例:淘宝评论爬取
要求:将评论中的评论内容,手机详情和用户昵称爬取存储写入json文件。
url='https://rate.tmall.com/list_detail_rate.htm?itemId=560257961625&spuId=893336129&sellerId=1917047079&order=3¤tPage=3&append=0&content=1&tagId=&posi=&picture=&ua=098%23E1hvmpvBvoGvUpCkvvvvvjiPPs5vtjibP2FvljrCPmPptjlWn2cOsj1UPsLW0jY8PghCvCB4cJrUyn147DIsWKwG8lID75qNh46Cvvyv2bgjtJZvO6ArvpvEvC2MmERLvhK99phv2HiJFMwXzHi47k7IzT6CvCvwhHeCeiIv%2B48nrsw6Zf5B3QhvCvvhvvm5vpvhvvmv9FyCvvpvvvvvvphvC9vhvvCvpbyCvm9vvvvvphvvvvvv99CvpvF4vvmmvhCv2UvvvUUvphvUgQvv99CvpvFFuphvmvvv9bUui7cWkphvC99vvOC0ppyCvhQUlC6vCld6u70xdByapbmxfwkK5ehhfw1l%2BboJEcqUaNspV13gprLO6nv7RAYVyO2vqbVQWl4v1WFIRfU6pwet9E7r5C69D7zZa40A2QhvCvvvvvmrvpvEvvp19aMNvmxhRphvCvvvvvvtvpvhvvvvvv%3D%3D&needFold=0&_ksTS=1535091159955_1320&callback=jsonp1321'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
}
request=urllib.request.Request(url,headers=headers)
json_text=urllib.request.urlopen(request).read().decode()
#原始的json串存在内容:在开始的位置出现了一些字符数据。
json_text=json_text.strip('\r\njsonp1321()')
#将json格式的字符串转换成json对象
json_obj=json.loads(json_text)
#进行json解析
rate_list=json_obj['rateDetail']['rateList']
my_rate_list=[] #存储解析后的内容
for rate in rate_list:
rate_content=rate['rateContent']
phone_detail=rate['auctionSku']
userName=rate['displayUserNick']
rate_dic={
'评论内容':rate_content,
'商品详情':phone_detail,
'用户昵称':userName
}
my_rate_list.append(rate_dic)
#将解析到的内容(json对象)写入磁盘
json.dump(my_rate_list,open('./rate.json','w',encoding='utf-8'),ensure_ascii=False)
评论 (0)