MOOC–Python网络爬虫和信息提取(第11次开课)
网络爬虫之实战 21.08.03
目录
文章目录
测验3: Python网络爬虫之提取 (第3周)
1、以下不是正则表达式优势的选项是:
A、简洁
B、特征表达
C、实现自动化脚本
D、一行胜千言
正确答案 C
尽管正则表达式可用于自动化脚本,但不直接体现自动脚本的作用。
2、正则表达式:^ [A-Za-z\d]+$的含义是什么?
A、由26个字母和数字组成的字符串
B、一个26个字母构成的字符串
C、由26个字母和特殊字符d组成的字符串
D、由26个字母组成的字符串
正确答案 A
以下这些操作符是正则表达式中最常用的:
3、正则表达式:\d{3}-\d{8}|\d{4}-\d{7}能匹配哪个?
A、010-12345678
B、01012345678
C、010-1234567
D、0521-12345678
正确答案 A
\d{3}-\d{8}|\d{4}-\d{7}
表示:3个数字-8个数字 或者 4个数字-7个数字
4、re库可以使用如下方式表示正则表达式:r’[1-9]\d{5}’,其中r是什么意思?
A、开始位置标记
B、正则表达式标记
C、强制标记
D、原生字符串标记
正确答案 D
字符串包括:普通字符串和原生字符串,原生字符串中没有转义符(\)。
5、正则表达式:^ [A-Za-z]+$的含义是什么?
A、由字母a和z、A和Z组成的字符串
B、由26个字母和特殊字符d组成的字符串
C、由26个字母和数字组成的字符串
D、由26个字母组成的字符串
正确答案 D
6、正则表达式:^-?\d+$的含义是什么?
A、一个整数形式的字符串
B、由26个字母和数字组成的字符串
C、一个带有负号的数字字符串
D、由26个字母组成的字符串
正确答案 A
7、正则表达式:1[1-9][0-9]$的含义是什么?
A、整数形式的字符串
B、正整数形式的字符串
C、由0到9数字组成的字符串
D、数字和*组成的字符串
正确答案 B
8、正则表达式:[1-9]\d{5}的含义是什么?
A、长度为5的正整数形式字符串
B、由0到9数字组成的字符串
C、整数形式的字符串
D、首位不为0的6长度数字形式字符串
正确答案 D
9、正则表达式:[\u4e00-\u9fa5]的含义是什么?
A、一个在\u4e00到\u9fa5之间的字符
B、由\u4e00到\u9fa5中一个或多个字符组成的字符串
C、由\u4e00到\u9fa5字符组成的字符串
D、\u4e00或\u9fa5
正确答案 A
10、Beautiful Soup库与re库之间关系,描述正确的是:
A、这两个库没有关系
B、re库能实现HTML解析,功能上与Beautiful Soup库类似
C、Beautiful Soup库中可以加载re库
D、re库中可以加载Beautiful Soup库
正确答案 A
两者没关系,re库以字符串模式匹配与检索方式提取信息,与Beautiful Soup库不同。
单元7:Re(正则表达式)库入门
7.1 正则表达式的概念
正则表达式是用来简洁表达一组字符串的表达式
应用:
- 表达文本类型的特征
- 查找或替换一组字符串
- 匹配字符串的全部或部分(主要应用)
正则表达式的使用:
编译:将符合正则表达式语法的字符串转换成正则表达式特征。
7.2 正则表达式的语法
正则表达式语法由字符和操作符构成
操作符 | 说明 | 实例 |
---|---|---|
. | 表示任何单个字符 | |
[ ] | 字符集,对单个字符给出取值范围 | [abc]表示a、b、c,[a-z]表示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)表示anc、def |
\d | 数字,等价于[ 0-9 ] | |
\w | 单词字符,等价于[A-Za-z0-9_] |
正则表达式的实例:
7.3 Re库的基本使用
7.3.1 正则表达式的表示类型
raw string类型(原生字符串):r’text’,r’[1-9]\d{5}’
rawstring是不包含对转义符再次转义的字符串
string 类型:‘[1-9]\d’、‘\d{3}-\d{8}|\d{4}-\d{7}’
7.3.2 Re库主要功能函数
函数 | 说明 |
---|---|
re.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
re.match() | 从一个字符串的开始位置起匹配正则表达式,返回match对象 |
re.findall() | 搜索字符串,以列表类型返回能匹配的子串 |
re.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
re.finditer() | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象 |
re.sub() | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
7.3.3 Re库主要功能函数(详解)
re.search(pattern, string, flags=0)
在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
- pattern :正则表达式的字符串或原生字符串表示
- string :待匹配字符串
- flags :正则表达式使用时的控制标记
常用标记 | 说明 |
---|---|
re.I re.IGNORECASE | 忽略正则表达式的大小写,[A-Z]能够匹配小写字符 |
re.M re.MULTINE | 正则表达式中的^操作符能够将给定字符串的每行当做匹配开始 |
re.S re.DOTALL | 正则表达式中的 . 操作能够匹配所有字符(默认仅匹配除换行符以外的所有字符) |
# re.search()
import re
match = re.search(r'[1-9]\d{5}','BIT 100081')
if match:
print(match.group(0)) # 100081
re.match(pattern, string, flags=0)
从一个字符串的开始位置起匹配正则表达式,返回match对象
- pattern :正则表达式的字符串或原生字符串表示
- string :待匹配字符串
- flags :正则表达式使用时的控制标记
# re.match()
import re
match = re.match(r'[1-9]\d{5}','BIT 100081')
if match:
print(match.group(0)) # 空
match2 = re.match(r'[1-9]\d{5}','100081 BIT')
if match2:
print(match2.group(0)) # 100081
re.findall(pattern, string, flags=0)
搜索字符串,以列表类型返回能匹配的子串
- pattern :正则表达式的字符串或原生字符串表示
- string :待匹配字符串
- flags :正则表达式使用时的控制标记
# re.findall()
import re
ls = re.findall(r'[1-9]\d{5}','BIT 100081 TSU100084')
print(ls) # ['100081', '100084']
re.split(pattern, string, maxsplit=0,flag=0)
将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
- pattern :正则表达式的字符串或原生字符串表示
- string :待匹配字符串
- maxsplit:最大分割数,剩余部分作为最后一个元素输出
- flags :正则表达式使用时的控制标记
# re.split()
import re
print(re.split(r'[1-9]\d{5}', 'BIT 100081 TSU100084'))
# ['BIT ', ' TSU', '']
print(re.split(r'[1-9]\d{5}', 'BIT 100081 TSU100084', maxsplit=1))
# ['BIT ', ' TSU100084']
re.finditer(pattern, string, flags=0)
搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象
- pattern :正则表达式的字符串或原生字符串表示
- string :待匹配字符串
- flags :正则表达式使用时的控制标记
# re.finditer()
import re
for m in re.finditer(r'[1-9]\d{5}', 'BIT 100081 TSU100084'):
if m:
print(m.group(0))
# 100081
# 100084
re.sub(pattern, repl, string, count=0, flags=0)
在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
- pattern :正则表达式的字符串或原生字符串表示
- string :待匹配字符串
- flags :正则表达式使用时的控制标记
# re.sub()
import re
print(re.sub(r'[1-9]\d{5}', ':zipcode', 'BIT 100081 TSU100084'))
# BIT :zipcode TSU:zipcode
print(re.sub(r'[1-9]\d{5}', ':zipcode', 'BIT 100081 TSU100084', count=1))
# BIT :zipcode TSU100084
7.3.4 Re库的等价用法
regex = re.compile(pattern, falgs=0)
将正则表达式的字符串编译成正则表达式对象
函数式用法:一次性操作
rst = re.search(r’[1-9]\d{5}’,‘BIT 100081’)
面向对象用法:编译后的多次操作
pat = re.compiler(r’[1-9]\d{5}’)
rest = pat.search(‘BIT 100081’)
7.4 Re库的match对象
Match对象是一次匹配的结果,包含匹配的很多信息
type(match) # <class ‘re.Match’>
7.4.1 Match对象的属性
属性 | 说明 |
---|---|
.string | 待匹配的文本 |
.re | 匹配时使用的patter对象(正则表达式) |
.pos | 正则表达式搜索文本的开始位置 |
.endpos | 正则表达式搜索文本的结束位置 |
7.4.1 Match对象的方法
方法 | 说明 |
---|---|
.group(0) | 获得匹配后的字符串 |
.start() | 匹配字符串在元氏字符串的开始位置 |
.end() | 匹配字符串在原始字符串的结束位置 |
.span() | 返回(.start(),end()) |
import re
m = re.search(r'[1-9]\d{5}', 'BIT100081 TSU100084')
print('m.string : {}\nm.re : {}\nm.pos : {}\nm.endpos : {}\n'.format(m.string,m.re,m.pos,m.endpos))
print('m.group(0) : {}\nm.start() : {}\nm.end() : {}\nm.span : {}\n'.format(m.group(0),m.start(),m.end(),m.span()))
# 输出结果:
# m.string : BIT100081 TSU100084
# m.re : re.compile('[1-9]\\d{5}')
# m.pos : 0
# m.endpos : 19
# m.group(0) : 100081
# m.start() : 3
# m.end() : 9
# m.span : (3, 9)
7.5 Re库的贪婪匹配和最小匹配
贪婪匹配:
Re库默认采用贪婪匹配,即输出匹配最长的子串
最小匹配:
输出匹配最短的子串,可以通过在操作符后增加?变成最小匹配
操作符 | 说明 |
---|---|
*? | 前一个字符0次或无限次扩展,最小匹配 |
+? | 前一个字符1次或无限次扩展,最小匹配 |
?? | 前一个字符0次或1次扩展,最小匹配 |
{m,n}? | 扩展前一个字符m至n次(含n),最小匹配 |
单元8:实例2:淘宝商品比价定向爬虫
参考课程内容,只能爬取到淘宝的登录界面(淘宝的反爬虫机制),因而需要对访问的头文件做修改。
登录自己的淘宝账户,搜索如 书包 等相应商品,在搜索结果界面按F12打开开发者模式(本人用的chrome浏览器),一般浏览器右键源代码/检查也可
按照上图所示,可在网络Network中找到cookie内容,随后在代码的getHTMLText中添加cooki和user-agent(一般是Mozilla/5.0)即可
注意:有时需要对网页进行刷新才会出现该Name
整个过程即将自己账户的登录信息复制给头文件
# 参考代码
import requests
import re
def getHTMLText(url):
kv={'cookie':'需填入自己的cooike内容'
,'user-agent':'Mozilla/5.0'}
try:
r = requests.get(url, headers = kv,timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def parsePage(ilt, html):
try:
# 注:pycharm编译下双引号、冒号等前无需加 \
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(54 * i)
html = getHTMLText(url)
parsePage(infoList, html)
except:
continue
printGoodsList(infoList)
main()
单元9:实例3:股票数据定向爬虫
这里采用 证券之星网 获取股票列表,用 网易财经网 获取单股数据。
import requests
from bs4 import BeautifulSoup
import re
def getHTMLText(url,code='utf-8'):
kv = {'user-agent': 'Mozilla/5.0'}
try:
r = requests.get(url,headers = kv,timeout=30)
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('span')
for i in a:
try:
href = i.contents[0].attrs['href']
lst.append(re.findall(r"\d{6}", href)[0])
except:
continue
def getStockInfo(lst, stockURL, fpath):
count = 0
for stock in lst:
url = stockURL + '0'+ stock + ".html"
html = getHTMLText(url)
try:
if html == "":
continue
infoDict = {}
soup = BeautifulSoup(html, 'html.parser')
# 网易财经单股使用数据文本形式保存,可以通过列表形式打印
# name,code,price,change,yesteday,today,high,low
div = soup.body.find('div', attrs={'class': 'relate_stock clearfix'})
stockInfo = div('script')[0].string.strip().split(',')
infoDict['股票名称'] = eval(re.search(r'name: \'.*\'', stockInfo[0]).group(0).split(':')[1])
infoDict['股票代码'] = eval(re.search(r'code: \'\d{6}\'', stockInfo[1]).group(0).split(":")[1])
infoDict['现价'] = eval(re.search(r'price: \'.*\'', stockInfo[2]).group(0).split(":")[1])
infoDict['涨跌幅'] = re.search(r'change: \'.*%', stockInfo[3]).group(0).split("'")[1]
infoDict['昨收'] = eval(re.search(r'yesteday: \'.*\'', stockInfo[4]).group(0).split(":")[1])
infoDict['今开'] = eval(re.search(r'today: \'.*\'', stockInfo[5]).group(0).split(":")[1])
infoDict['最高'] = eval(re.search(r'high: \'.*\'', stockInfo[6]).group(0).split(":")[1])
infoDict['最低'] = eval(re.search(r'low: \'.*\'', stockInfo[7]).group(0).split(":")[1])
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 = 'http://quote.stockstar.com/stock/stock_index.htm'
stock_info_url = 'http://quotes.money.163.com/'
output_file = 'BaiduStockInfo.txt'
slist = []
getStockList(slist, stock_list_url)
getStockInfo(slist, stock_info_url, output_file)
main()
资料来源:
Python网络爬虫与信息提取_北京理工大学_中国大学MOOC(慕课) https://www.icourse163.org/course/BIT-1001870001
0-9 ↩︎