第四章 网络爬虫之规则(Re正则表达式篇)
● Re(正则表达式)库入门
- 正则表达式简介
1、RE(regular expression,regex,正则表达式)是用来简洁表达一组字符串的表达式。
2、RE库理解;1)通用的字符串表达框架;
2)简洁表达一组字符串的表达式;
3)针对字符串表达“简洁”和“特征”思想的工具;
4)判断某字符串的特征归属。
3、正则表达式常用于文本处理:
1)表达文本类型的特征(病毒、人侵等);
2)同时查找或替换一组字符串;
3)冷匹配字符串的全部或部分。
4、正则表达式的使用——编译:将符合正则表达式语法的字符串转换成正则表
达式特征。 - 正则表达式语法
1、正则表达式语法由字符和操作符构成。例:P(Y|YT|YTH|YTHO)?N
2、正则表达式常用操作符
实例:操作符 说明 实例 . 表示任何单个字符 [ ] 字符集,对单个字符给出取值范围 [abc]表示a、b、c,[a-2]表示a到z单个字符 [^] 非字符集,对单个字符给出排除范围 [^abc]表示非a或b或c的单个字符 * 前一个字符0次或无限次扩展 abc*表示ab、abc、abcc、abccc等 + 前一个字符1次或无限次扩展 abc+表示abc、abcc、abccc等 ? 前一个字符0次或1次扩展 abc?表示ab、abc | 左右表达式任意一个 abc|def表示abc、def {m} 扩展前一个字符m次 ab{2)c表示abbc {m,n} 扩展前一个字符m至n次(含n) ab{1,2}c表示abc、abbc ^ 匹配字符串开头 ^abc表示abc且在一个字符串的开头 $ 匹配字符串结尾 abc$表示abc且在一个字符串的结尾 () 分组标记,内部只能使用|操作符 (abc)表示abc,(abc|def)表示abc、def \d 数字,等价于[0-9] \w 单词字符,等价于[A-Za-z0-9_]
精确写法:IP地址字符串形式的正则表达式(IP地址分4段,每段0-255)
- Re库的基本使用
1、Re库介绍:Re库是Python的标准库,主要用于字符串匹配。
调用方式: import re
2、正则表达式的表示类型
1) raw string类型(原生字符串类型,不包含转义符的字符串)—re库采用raw string类型表示正则表达式,表示为:r‘text’
2)string类型—使用转移字符\表示\,更繁琐
3、Re库主要功能函数
1)re.search()方法
使用格式:re.search(pattern, string, flags=0)
-pattern:正则表达式的字符串或原生字符串表示
-string:待匹配字符串
-flags:正则表达式使用时的控制标记
flags:正则表达式使用时的控制标记
>>> import re >>> match = re.search(r'[1-9]\d{5}','BIT 100081') >>> if match: ... print (match.group(0)) ... 100081
2)re.match()方法
使用格式:re.match(pattern, string, flags=0)
-pattern:正则表达式的字符串或原生字符串表示
-string:待匹配字符串
-flags:正则表达式使用时的控制标记
>>> import re
>>> match = re.match(r'[1-9]\d{5}','BIT 100081')
>>> if match:
... print(match.group(0))
...
>>> match.group(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> match = re.match(r'[1-9]\d{5}','100081 BIT')
>>> if match:
... print(match.group(0))
...
100081
注:空变量没有group(),使用正则表达式匹配结果,先用if判断结果是否为空。
3)re.findall()方法
使用格式:re.findall(pattern, string, flags=0)
-pattern:正则表达式的字符串或原生字符串表示
-string:待匹配字符串
-flags:正则表达式使用时的控制标记
>>> import re
>>> ls = re.findall(r'[1-9]\d{5}','BIT100081 TSU100084')
>>> ls
['100081', '100084']
4)re.split()方法
使用格式:re.split(pattern, string,maxsplit=0, flags=0)
-pattern:正则表达式的字符串或原生字符串表示
-string:待匹配字符串
-maxsplit:最大分割数,剩余部分作为最后一个元素输出
-flags:正则表达式使用时的控制标记
>>> import re
>>> re.split(r'[1-9]\d{5}','BIT100081 TSU100084')
['BIT', ' TSU', '']
>>> re.split(r'[1-9]\d{5}','BIT100081 TSU100084',maxsplit=1)
['BIT', ' TSU100084']
5)re.finditer()方法
使用格式:re.finditer(pattern, string, flags=0)
-pattern:正则表达式的字符串或原生字符串表示
-string:待匹配字符串
-flags:正则表达式使用时的控制标记
>>> import re
>>> for match in re.finditer(r'[1-9]\d{5}','BIT100081 TSU100084'):
... if match:
... print(match.group(0))
...
100081
100084
6)re.sub()方法
使用格式:re.sub(pattern,repl, string,count=0, flags=0)
-pattern:正则表达式的字符串或原生字符串表示
-repl:替换匹配字符串的字符串
-string:待匹配字符串
-count:匹配的最大替换次数
-flags:正则表达式使用时的控制标记
>>> import re
>>> re.sub(r'[1-9]\d{5}',':zipcode','BIT100081 TSU100084')
'BIT:zipcode TSU:zipcode'
4、Re库的等价用法
1)函数式用法:一次性操作
例:res = re.search(r’[1-9]\d{5}’,‘BIT 100081’)
2)面向对象用法:编译后的多次操作
例:match = re.compile(r’[1-9]\d{5}’)
res = match.search(‘BIT 100081’)
re.compile(pattern, flags=0)
作用:将正则表达式的字符串形式编译成正则表达式对象
-pattern:正则表达式的字符串或原生字符串表示
-flags:正则表达式使用时的控制标记
- Re库的match对象
1、match对象属性:
2、match对象的方法:
>>> import re
>>> match = re.search(r'[1-9]\d{5}','BIT100081 TSU100084')
>>> match.string
'BIT100081 TSU100084'
>>> match.re
<_sre.SRE_Pattern object at 0x028092C0>
>>> match.pos
0
>>> match.endpos
19
>>> match.group(0)
'100081'
>>> match.start()
3
>>> match.end()
9
>>> match.span()
(3, 9)
- Re库的的贪婪匹配与最小匹配
1、贪婪匹配:Re库默认釆用贪娈匹配,即输出匹配最长的子串。
>>> match = re.search(r'PY.*N','PYANBNCNDN')
>>> match.group(0)
'PYANBNCNDN'
2、最小匹配:输出最短的字符串
最小匹配操作符
● Re(正则表达式)库实例
- 淘宝商品比价定向爬虫
1、功能描述
目标:获取淘宝搜索页面的信息,提取其中的商品名称和价格。
理解:淘宝的搜索接口、翻页的处理
技术路线::requests-re
2、规律查找:搜索书包
3、定向爬虫的可行性—robot.txt
4、程序结构设计
步骤1:提交商品搜索请求,循环获取页面。
步骤2:对于每个页面,提取商品名称和价格信息。
步骤3:将信息输出到屏幕上。
5、程序设计(仅供参考):
import requests
import re
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def parsePage(ilt, html):
try:
plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html)
tlt = re.findall(r'\"raw_title\"\:\".*?\"',html)
for i in range(len(plt)):
price = eval(plt[i].split(':')[1])
title = eval(tlt[i].split(':')[1])
ilt.append([price , title])
except:
print("")
def printGoodsList(ilt):
tplt = "{:4}\t{:8}\t{:16}"
print(tplt.format("序号", "价格", "商品名称"))
count = 0
for g in ilt:
count = count + 1
print(tplt.format(count, g[0], g[1]))
def main():
goods = '书包'
depth = 3
start_url = 'https://s.taobao.com/search?q=' + goods
infoList = []
for i in range(depth):
try:
url = start_url + '&s=' + str(44*i)
html = getHTMLText(url)
parsePage(infoList, html)
except:
continue
printGoodsList(infoList)
main()
- 股票数据定向爬虫
1、功能描述
目标:获取上交所和深交所所有股票的名称和交易信息
输出:保存到文件中
技术路线:requests-bs4-re
2、候选数据网站的选择
选取原则:股票信息静态存在于HTML页面中,非js代码生成,没有 Robots协议限制。
选取方法:浏览器F12,源代码查看等。
选取心态:不要纠结于某个网站,多找信息源尝试。
3、程序的结构设计
步骤1:从东方财富网获取股票列表
步骤2:根据股票列表逐个到百度股票获取个股信息
步骤3:将结果存储到文件
4、程序设计(仅供参考):
import requests
from bs4 import BeautifulSoup
import traceback
import re
def getHTMLText(url, code="utf-8"):
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = code
return r.text
except:
return ""
def getStockList(lst, stockURL):
html = getHTMLText(stockURL, "GB2312")
soup = BeautifulSoup(html, 'html.parser')
a = soup.find_all('a')
for i in a:
try:
href = i.attrs['href']
lst.append(re.findall(r"[s][hz]\d{6}", href)[0])
except:
continue
def getStockInfo(lst, stockURL, fpath):
count = 0
for stock in lst:
url = stockURL + stock + ".html"
html = getHTMLText(url)
try:
if html=="":
continue
infoDict = {}
soup = BeautifulSoup(html, 'html.parser')
stockInfo = soup.find('div',attrs={'class':'stock-bets'})
name = stockInfo.find_all(attrs={'class':'bets-name'})[0]
infoDict.update({'股票名称': name.text.split()[0]})
keyList = stockInfo.find_all('dt')
valueList = stockInfo.find_all('dd')
for i in range(len(keyList)):
key = keyList[i].text
val = valueList[i].text
infoDict[key] = val
with open(fpath, 'a', encoding='utf-8') as f:
f.write( str(infoDict) + '\n' )
count = count + 1
print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
except:
count = count + 1
print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
continue
def main():
stock_list_url = 'https://quote.eastmoney.com/stocklist.html'
stock_info_url = 'https://gupiao.baidu.com/stock/'
output_file = 'D:/BaiduStockInfo.txt'
slist=[]
getStockList(slist, stock_list_url)
getStockInfo(slist, stock_info_url, output_file)
main()
5、程序优化之处—提高用户体验
1)速度提高:编码识别的优化
2)体验提高:增加动态进度显示