Python四大解析库
- 编码是信息从一种形式或格式转换为另一种形式的过程,解码则是编码的逆过程。(乱码的诞生就是编解码不一致,在网页中根据charset=""确定编解码格式)
- encode编码、decode解码、encoding是requests的内置编码器。
- ASCII最简单字符集、UTF-8是Unicode的轻量级实现、Unicode现行最大字符集。
ASCII
- 计算机内部,所有信息最后都是一个二进制。
- 一个字节byte是8位二进制,二进制有0和1两种状态,所以一个字节有256种状态,每个状态对应一个符号。ascii一种256个符号。
- 上个世纪,美国制定了这套编码,英语字符和二进制一一对应,沿用到现在。
- 比如空格是32,大写字母A是65,字符一共128个,这些字符只占用一个字节的后边7位,最前边统一为0。
Unicode
- 100多万个符号,把世界上所有的符号融入其中,每个符号都有独一无二的编码。
- 比如U+0639代表阿拉伯字母Ain,U+4E25代表汉子严。
- 不过存在的问题是,unicode转化为二进制需要很多空间,比如严的16进制4E25转化为二进制有15位(100111000100101),至少两个字节。其他更大的符号可能有5-6个字节。
- 因为字母只需要二进制的七位,所以在unicode中,一般字符三到四个字节,会有空间浪费,文本文件因此大出好几倍,是无法接受的。
UTF8
- 目前最常见的unicode实现方式,还有utf-16,utf-32等等。
- 对于单字节的符号,字节第一位为0,后边7位是unicode码。
- 对于英文字母,utf8和unicode是一样的。
- 对于n字节的符号,第一个字节前边的n位都设为1,n+1为设为0,后边字节的前两位一律为10。剩下的没有提及的为unicode。
#编码解码一般是一一对应的,编解码格式应该一致,不一致可能导致编码问题-乱码
text="这是一段测试文本"
html=text.encode('utf8')
print(html)
html=html.decode('utf8')
print(html)
'''
b'\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe6\xae\xb5\xe6\xb5\x8b
\xe8\xaf\x95\xe6\x96\x87\xe6\x9c\xac'
这是一段测试文本
'''
应用场景
- 在Python中,编码解码常见于
1、写入文本文件。
2、获取网页页面。
text="这是一段测试文本"
with open('file.txt',encoding='utf-8') as f:
f.write(text)
import requests
def get(url):
html=requests.get(url)
html.encoding='utf-8'
print(html.text)
必备字符串操作函数
函数 | 定义 |
---|---|
str.replace() | 字符串替换 |
str.translate() | ord映射替换 |
str.split() | 分割字符串 |
str.startswith() | 判断字符串开头 |
str.endswith() | 判断字符串结尾 |
str.join() | 合并字符串 |
str.strip() | 去除首尾不需要的字符 |
%s%d----format----f | 字符串填充 |
import requests
url='http://www.baidu.com'
url=url.replace('baidu','python')
print(url)
intab='aeiou'
outtab='12345'
trantab=str.maketrans(intab,outtab)#定义策略
str='this is string example....wowppp!!!'
print(str.translate(trantab))
'''
'''
import requests
url='http://www.baidu.com/python'
for u in url.split('/'):
if u.startswith('www'):
print(u)
'''
www.baidu.com
'''
import requests
url='http://www.baidu.com/python'
for u in url.split('/'):
if u.endswith('m')
print(u)
'''
www.baidu.com
'''
import requests
urls=['python','3.7','hello','world']
s='|'.join(urls)
print(s)
'''
python|3.7|hello|world
'''
import requests
urls=' hello '
print(urls.strip())
import requests
keyword='python'
urls='http://www.baidu.com?wd=%s'%keyword #c++版本
urls='http://www.baidu.com?wd={}'.format(keyword) #python3.6版本
urls=f'http://www.baidu.com?wd={keyword}' #最新使用方法
print(urls)
正则表达式(学爬虫必须精通)
- regular expression/regex/RE
- 正则表达式是用来简洁表达一组字符串的表达式
- 通用的字符串表达框架
- 针对字符串表达“简洁”和“特征”思想的工具
- 判断某字符串的特征归属(病毒、入侵等)
- 同时查找或替换一组字符串
- 匹配字符的全部或部分区域
- 编译:将符合正则表达式语法的字符串转换成正则表达式特征
'PN' 'PYTN' 'PYTHN'
'PYN' 'PYTHON'
正则表达式为:
P(Y|YT|YTH|YTHO)?N
'PY'开头
后续存在不多于10个字符
后续字符不能是'P'或'Y'
'PYABC'对
'PYKXYZ'错
正则表达式为:
PY[^PY]{0,10}
正则表达式其他使用方法参考下面链接
https://tool.oschina.net/uploads/apidocs/jquery/regexp.html
补充以下部分
^[A-Za-z]+$ 由26个字母组成的字符串
^[A-Za-z0-9]+$ 由26个字母和数字组成的字符串
^-?\d+$ 整数形式的字符串
^[0-9]*[1-9][0-9]*$ 正整数形式的字符串
[1-9]\d{5} 中国境内邮政编码,6位
[\u4e00-\u9fa5] 匹配中文字符
\d{3}-\d{8}|\d{4}-\d{7}
Re库介绍
re库采用raw string类型表示正则表达式,表示为:r’text’
raw string 是不包含转义符的字符串
re库默认采用贪婪匹配,即输出匹配最长的子串,用?号最小匹配
- re.match(pattern,string,flag)
- 从文本开头开始匹配
- 只能匹配一个
|修饰符|描述|
|–|--|
|re.I|使匹配对大小写不敏感|
|re.L|做本地化识别(locale-aware)匹配|
|re.M|多行匹配,影响^和$匹配每行的开始和结束|
|re.S|使.匹配包括换行在内的所有字符|
|re.U|根据unicode字符集解析字符,这个标志影响\w \W \b \B|
|re.X|该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解|
import re
text='<a href="https://www.baidu.com"">'\
'python 正则表达式</a>'
result=re.match('<a href=".*?",text,re.I)
print(result)
- re.search(pattern,string,flag)
- 匹配整个字符串
- 只能匹配一个
- 全局匹配
match和 search得到的是正则表达式匹配对象,提取结果需要使用特定函数。
- group()匹配的正则字符串
- groups()返回包含字符串元组
import re
text='<a href="https://www.baidu.com"">'\
'python 正则表达式</a>'
result=re.search('href=".*?",text,re.I)
print(result.group())
# href="www.baidu.com
import re
text='<a href="https://www.baidu.com"">'\
'python 正则表达式</a>'
result=re.search('href="(.*?)",text,re.I)
print(result.groups())
# (‘www.baidu.com’)
- re.compile(pattern[flags])
- 编译正则表达式,可以提供match和search使用
import re
text='<a href="https://www.baidu.com"'\
' href="www.news163.com">'\
'Python 正则表达式</a>'
res=re.compile('href="(.*?)"',re.I|re.S)
print(rea.search(text))
- re.findall(string[,pos[,endpos]])
- 匹配所有合规字符串
import re
text='<a href="https://www.baidu.com"'\
' href="www.news163.com">'\
'Python 正则表达式</a>'
res=re.compile('href="(.*?)"',re.I|re.S)
print(rea.findall(text))
-
finditer与findall类似但是个迭代器,配合for循环,减轻对CPU的压力,返回一个迭代器对象
-
返回的结果少就用findall,返回结果多就用finditer
-
re.sub(pattern,text,flag)
-
相当于replace
import re
text='<a href="https://www.baidu.com"'\
' href="www.news163.com">'\
'Python 正则表达式</a>'
re.sub("href","url",text,count=0,re.I|re.S) #count表示匹配的最大替换次数
print(res)
'''
我们可以用Python自带的replace或者translate解决,一般不常用。
只常用于自然语言处理。
'''
- re.split() 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
import re
re.split(r'[1-9]\d{5}','BIT100081 TSU100084')
['BIT', ' TSU', '']
re.split(r'[1-9]\d{5}','BIT100081 TSU100084',maxsplit=1) #关注maxsplit参数
['BIT', ' TSU100084']
Re库的Match对象
属性 | 说明 |
---|---|
.string | 带匹配对象 |
.re | 匹配时使用的pattern对象(正则表达式) |
.pos | 正则表达式搜索文本的开始位置 |
.endpos | 正则表达式搜索文本的结束位置 |
方法 | 说明 |
---|---|
.group(0) | 获得匹配后的字符串 |
.start() | 匹配字符串在原始字符串的开始位置 |
.end | 匹配字符串在原始字符串的结束位置 |
.span() | 返回(.start(),.end()) |
XPATH(Python解析器)
- 按照能力排名:正则表达式>xpath>bs4>pyquery
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
. . | 选取当前节点的父节点 |
@ | 选取属性 |
contains() | //div[contains(@id,‘in’)] |
text() | //a[text()=‘baidu’] |
starts-with() | //div[starts-with(@id,‘in’)] |
not() | //input[not(contains(@class,‘a’))] |
import requests
from lxml import etree
html=requests.get("url")
soup=etree.HTML(html.text)
titles=soup.xpath('//div[@class="design"]/a/@title') #属性内容
# titles=soup.xpath('//div[@class="design"]/a/text()')内容
https://www.runoob.com/xpath/xpath-tutorial.html
xpath菜鸟教程
import requests
from lxml import etree
def get_html():
headers={
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Cookie': 'PSTM=1536112940; BAIDUID=6619906C417FC06F82AB8ECE44FCE157:FG=1; BIDUPSID=AA6099717F106CF6EB3EF41F0C0F1F87; MCITY=-131%3A; BDUSS=mJJT0lqeDV2czBFUjNTNGY5RmptUG9Yd1lnT0trcTJrWU9RUm04em51d1dEM2RlRVFBQUFBJCQAAAAAAAAAAAEAAADoY-olwLa358T90PcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABaCT14Wgk9eZ; __guid=136081015.1962738096307300400.1584165960379.7942; bce-login-type=PASSPORT; BD_HOME=1; monitor_count=1; BD_UPN=12314753; delPer=0; BD_CK_SAM=1; PSINO=1; H_PS_PSSID=1421_21101_31186_30908_31218_30823_31086_26350_31164_22160; H_PS_645EC=45edsTCNqrukfXv1QyIdr9GXfUvbBkgbTd9TmHyuI1kYuOxIYA9mW6tP%2FfSxsWz2FVuC; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; COOKIE_SESSION=1454621_0_9_9_3_3_0_7_9_2_1_0_0_0_16_0_1584165961_0_1586050118%7C9%2316774062_5_1581930231%7C2; BDRCVFR[feWj1Vr5u3D]=mk3SLVN4HKm; BDSVRTM=30',
'Host': 'www.baidu.com',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'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.baidu.com/s'
html=requests.get(url,params={'wd':'python'},headers=headers)
if html.status_code==200:
return get_content(html)
else:
print('get_html_error')
def get_content(html):
soup=etree.HTML(html.text)
elements=soup.xpath('//div[@id="content_left"]/div[contains(@class,"result")]')
for e in elements:
title=e.xpath('h3/a/text()')
link=e.xpath('h3/a/@href')
if title and link: #与老师的相比增加了一个防止内容为空的判断
print(''.join(title),link)
if __name__ == '__main__':
get_html()
'''
如果不增加if title and link进行判断会出现
list index out of range错误
'''