The website is the API...要获取网站内容,只要把网站当成API就可以了。
requests库获取网页信息---》Beautiful Soup解析提取到信息的内容---》利用re库正则表达式提取其中某部分的关键信息----》Scrapy*网络爬虫
requests库的更多信息参考:http://cn.python-requests.org/zh_CN/latest/
pip install requests # doc命令行下输入安装即可,测试例子:
>>> import requests #导入requests库
>>> r = requests.get(url="http://www.baidu.com",timeout=30) #返回一个Python对象r
>>> r.status_code
200 #状态码200 Ok
>>> r.encoding = r.apparent_encoding #
>>> r.text
>>> type(r) #查看r类型,返回的是一个对象
get方法:
r = requests.get(url)
r 是服务器返回的一个包含服务器所有资源的Response对象,requests.get(url)是Python构造的一个向服务器请求资源的Request对象。
requests.get(url,params=None,**kwargs)
Response对象的常用属性
网络链接有风险,所以需要进行异常处理。
requests库的异常:
r.raise_for_status() #判断r若果不是200,产生异常requests.HTTPError异常
爬去网页通用代码框架:
import requests
def getHTMLText(url):
try:
r = requests.get(url,timeout=30)
r.raise_for_status() #请求不成功不是200,则引发HTTPError异常
r.encoding = r.apparent_encoding
return r.text
except:
return "异常了!"
if __name__ == "__main__":
url = "http://www.baidu.com"
print(getHTMLText(url))
http协议采用URL作为定位网络资源的标识,协议名://host[:port]/资源路径
一个URL对应服务器一个资源,http协议对资源的操作方法:每次操作都是独立和无状态的
和requests库的get、post、put、、、7个方法是一一对应的!!!
理解PATCH和PUT的区别:
PATCH优点是节省网络带宽啊!需要改服务器哪个字段就上传哪个字段就可!
Requests库的head方法:
>>> r = requests.head(url="http://www.jd.com",timeout=30) #可以以很少的网络流量获取网站大体信息
>>> r.headers #响应头
{'Content-Type': 'text/html', 'Connection': 'keep-alive', ...}
>>> r.text #所以API内容是空的
''
Requests库的post方法:控制参数是data
>>> payload = {'key1':'value1','key2':'value2'} #post的是一个字典,则自动编码为form表单
>>> r = requests.post(url='http://httpbin.org/post',data=payload,timeout=30)
>>> print(r.text) #返回json
{
......
"form": { #post的是字典或键值对,自动放在form表单下的
"key1": "value1",
"key2": "value2"
},
......
}
>>> print(r.json()) #返回的是一个Python字典
{... 'form': {'key1': 'value1', 'key2': 'value2'}, ......}
向URL post一个字符串,自动编码为data
>>> string = "skdkheh990" #post一个字符串,自动编码为data
>>> r = requests.post(url="http://httpbin.org/post",data = string)
>>> print(r.text) #返回json
{
......
"data": "skdkheh990", #post字符串自动存在data字段下
"form": {},
......
}
>>> r.json() #json()方法返回一个Python字典
{...... 'form': {}, 'url': 'http://httpbin.org/post', 'data': 'skdkheh990',......}
Requests库的put方法:
r = requests.request(method,url,**kwargs),request方法是基础方法
method:请求方式
**kwargs:控制方为参数,都是可选项
1.params:是字典或字节序列,作为参数增加到URL中,是get方法中使用
>>> kv = {'wd':'unittest'}
>>> r = requests.get(url='http://www.baidu.com',params=kv)
>>> r.status_code
200
>>> r.url
'http://www.baidu.com/?wd=unittest'
2.data:是字典、字节序列或文件对象,作为Request的内容,向服务器提交资源时使用,post、put、patch、delete方法使用
>>> dic ={'key1':'value1','key2':'value2'} #
>>> r = requests.post(url='http://httpbin.org/post',data=dic)#post提交到服务器不会显示在URL上
>>> body='stringafasdf'
>>> r = requests.post(url="http://httpbin.org/post",data=body)
3.json:是json格式的数据,作为Request的内容,提交内容是json串时,用到json=。。。,post等方法使用
>>> json1 ={'key1':'value1'}
>>> r = requests.post(url="http://httpbin.org/post",json=json1)
>>> print(r.text)
{
"args": {},
"data": "{\"key1\": \"value1\"}",
"files": {},
"form": {},
"json": { #json=json1会把数据提交到服务器的json域中
"key1": "value1"
},
......
}
post 控制参数用到data、json的区别:
4.headers:字典格式,用于定制http请求头
>>> header={'user-agent':'chrome/10'} #模拟浏览器向服务器发起请求就是用headers
>>> r = requests.request('POST',url="http://httpbin.org/post",headers=header)
5.cookies:是字典或cookieJar格式,Request中的cookie
6.auth :是元组类型,用于http认证功能
7.files:是字典类型,用于向服务器传输文件
>>> fs = {'file':open(r'E:\test.txt','rb')}
>>> r = requests.post(url="http://httpbin.org/post",files = fs,timeout=30)
>>> print(r.text)
{
"args": {},
"data": "",
"files": { #向服务器提交的文件保存到服务器的files域中
"file": "hahfhadfhadsfhhsdflahlowej[of567890987654567890987654345678"
},
"form": {},
......
}
8.timeout:设定超时时间,秒为单位
9.proxies:是字典类型,可以为我们爬取网页设定访问代理服务器,可以增加登录认证
>>> pxs = {'http':'http"//user:[email protected].:1234','https':'https://10.10.10.1.4321'}#设置2个代理,一个是http访问时使用的代理,另一个是https访问时使用的代理
>>> r = requests.get(url='http://www.baidu.com',proxies=pxs) #可以隐藏用户爬去网页时原来的IP地址信息
10.allow_redirects:Ture/False,默认是Ture,用于允不允许URL进行重定向
11.stream:Ture/False,默认是Ture,用于对获取的内容是否立即下载
12.verify:Ture/False,默认是Ture,用于验证SSL证书开关
13.cert:保存本地SSL证书路径的字段
网络爬虫的尺寸
网络爬虫的限制
网站是通过根据请求头中user-agent字段是否是浏览器来判断是否是爬虫爬取数据,通过headers={...}更改用户代理为浏览器就可以正常爬取数据了。
来源审查:判断user-agent进行限制
检查来访http协议头的user-agent域,只响应浏览器或友好爬虫的访问
发布公告:Robots协议(网络爬虫排除标准)
http://www.jd.com/robots.txt 京东网终的robots协议
robots协议是指:放在网站根目录下,用来告知爬虫哪些可以爬取哪些不能爬取数据。
爬取例子:
import requests
def getJd(url):
try:
header= {'user-agent':'Mozilla/5.0'} #定制请求头,用户代理
r = requests.get(url,headers=header,timeout=30)
r.raise_for_status() #不是200则爬出httperror
r.encoding = r.apparent_encoding
print(r.request.headers) #请求头
print(r.headers) #响应头
return r.text[:1000]
except:
return "异常了!"
if __name__ == "__main__":
url = "https://item.jd.com/4435312.html"
print(getJd(url))
图片爬取:
import requests
#爬取网上的图片、视频、动画等http://xxx.jpg/x.mp4/xxx.mp3
def gePicture(url,path):
try:
r = requests.get(url)
r.raise_for_status()
except:
return '出错了'
else:
#with打开一个文件,然后把爬取到的内容(2进制)写进这个文件中,r.content表示返回内容的2进制形式
with open(path,'wb') as f:
#将返回的二进制图片数据写进文件中
f.write(r.content)
f.close()
if __name__ == "__main__":
url = "http://xxx.mpv"
path = r'D:\screenshot\girl1.jpg'
gePicture(url,path)
图片爬取2:
import requests
import os
url = "https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1489415348&di=9d1919ad385f0b4afed19f83f1887e75&src=http://img.caixin.com/2014-03-24/1395633851186812.jpg"
root = "D://screen//"
path = root + url.split('/2014-03-24/')[-1]
try:
#文件夹不存在则新建一个
if not os.path.exists(root):
os.mkdir(root)
#文件不存在则保存,存在则打印‘已存在’
if not os.path.exists(path):
r = requests.get(url)
with open(path,'wb') as f :
f.write(r.content)
f.close()
print('文件保存成功!')
else:
print('文件已经存在!')
except:
print('爬取失败')
我们平时看到的人机交互方式,如:图像、文本框、点击按钮等等,浏览器向后台服务器提交请求的时候其实都是以链接的形式来提交的,只要通过浏览器的解析知道提交给后台的URL的形式那就可以用request库模拟浏览器向服务器做正式的提交。向网站提交数据时可以通过这种方式。
eg:在百度搜索框搜索内容其实是:http://www.baidu.com?wd='unittest'方式提交的
网络上任何内容都有一个URL,任何内容都是用URL定义的,都可以通过URL对网络上内容进行操作。
Beautiful Soup库可以将HTML、XML进行解析并且提取其中的相关信息,可以将提供给它的任何格式进行爬取并且可以进行树形解析。
pip install beautifulsoup4
安装成功后使用Beautiful Soup库,先import进:
from bs4 import BeautifulSoup (从bs4导进BeautifulSoup这个类)
soup = BeautifulSoup('
data
','html.parser')参数解析:
data
html.parser:对xml或html解析的解析器
使用例子:
>>> import requests
>>> r = requests.get(url='http://192.168.0.107/html5/girl.html')
>>> r.encoding = r.apparent_encoding
>>> demo = r.text #通过requests库获取API页面
>>> from bs4 import BeautifulSoup #从bs4导进BeautifulSoup类
>>> soup = BeautifulSoup(demo,'html.parser') #html.paeser是解析demo的解析器(对demo进行html的解析)
>>> print(soup.prettify()) #调用prettify()方法,下面则是成功解析了。
四大美女
...... ...... ...... ......
...
:标签Tagp:标签的name
属性:成对存在
Beauti Soup库的导入
from bs4 import BeautifulSoup
import bs4
html文档 == 标签树 == BeautifulSoup类 可以认为三者是等价的
>>>from bs4 import BeautifulSoup
>>>soup = BeautifulSoup('data','html.parser')
>>>soup1=BeautifulSoup(open(r'D:\demo.html'),'html.parser')
简单来说一个BeautifulSoup类对应一个html文档的全部内容。如上面的soup、soup1都对应一个html文档。
Beautiful Soup库的解析器
一般使用html.parser解析器就行了。
通过 soup.标签名 可获得该标签信息
当存在多个一样的标签时,默认返回第一个标签的信息。
>>> soup=BeautifulSoup(demo,'html.parser')
>>> soup.title
四大美女
>>> soup.a
通过 soup.标签.name 可获取标签的名字,以字符串形式返回
>>> soup.a.name #获取a标签的名字
'a'
>>> soup.a.parent.name #获取a标签的上级标签名字
'p'
>>> soup.a.parent.parent.name #获取a标签的上上级标签的名字
'hr'
通过
soup.标签.attrs 可获得标签的属性,以字典形式返回
>>> soup.a.attrs #获取a标签的属性,字典返回
{'href': 'http://www.baidu.com', 'target': '_blank'}
>>> tag = soup.a.parent
>>> tag.name #获取p标签的属性,字典返回
'p'
>>> tag.attrs
{'align': 'center'}
因为返回的是字典可以采用字典的方法对其进行信息的提取。
>>> for i,j in soup.a.attrs.items(): #for循环遍历字典
print(i,j)
href http://www.baidu.com
target _blank
>>> soup.a.attrs['href'] #获取某个key的value值
'http://www.baidu.com'
>>> soup.a.attrs.keys() #获取字典所有的keys
dict_keys(['href', 'target'])
>>> soup.a.attrs.values() #获取字典所有values
dict_values(['http://www.baidu.com', '_blank'])
通过
soup.标签.string 可以获取标签之间的文本,返回字符串
>>> soup.title.string #获取title表之间的文本
'四大美女'
>>> soup.a.string #获取a标签之间的文本,没有返回空
>>> soup.a
html中注释部分通过soup.html或soup不能显示出来,要查看html的注释怎么办?
>>> newsoup = BeautifulSoup('这是段落
','html.parser')
>>> newsoup.b.string
'这是注释!'
>>> newsoup.p.string
'这是段落'
>>> type(newsoup.b.string)
>>> type(newsoup.p.string)
树形结构:
属性及说明 返回的是列表
也就是说contents和children只获得当前节点的下一层的节点信息,而descendants可以获得一个节点后续的所有节点信息用于遍历
>>> soup.head
四大美女
>>> soup.head.contents #获取head标签下的儿子节点
['\n',
四大美女
]
>>> len(soup.body.contents) #通过len函数获取body标签的儿子节点个数
3
>>> for i in soup.body.children: #遍历body标签的儿子节点
print(i)
>>> for i in soup.body.descendants: #遍历body标签所有的儿子、子孙节点
print(i)
>>> soup.a.parents
>>> for i in soup.a.parents:
if i is None: #对a标签的所有先辈标签进行打印
print(i)
else:
print(i.name)
p
hr
body
html
[document]
平行遍历必须是发生在同一个父亲节点下的各节点间。
属性及说明迭代类型是需要通过循环遍历一个个打印出来的
在标签树中树形结构采用的是标签来组织,但标签之间的字符串也构成了标签的节点,也就是任何节点它的平行节点或儿子几点是存在string类型的所以不能一致认为它们都是标签。
>>> soup.p.next_sibling.next_sibling #p标签的下一个再下一个平行节点
四大美女
>>> soup.p.previous_sibling #p标签的上一个平行节点
'\n'
>>> soup.p.previous_sibling.previous_sibling #p标签的上一个再上一个的平行节点
>>> print(soup.a.prettify())
新闻
bs4库将任何读入的html文件或字符串都转换成“utf-8”编码。
XML
... 有内容时用一对标签表达
空元素时缩写形式,没内容
JSON
js对面向对象语言的表达形式,有类型的键值对。
对js等编程语言,可以把json直接当成编程语言的一部分,Python的或需要通过json库的dump()和load()方法字典和json的转换。
YAML
无类型的键值对,通过缩进表达所属关系,和Python很像。
通过-号表达并列关系
name:
-liyue
-liyu
-test
|表示整块数据,#注释
key:value
key:#comment 注释
-value1
-value2
key:
subkey:subvalue
融合方法:结合形式解析与搜索方法,提取关键信息。
例子:提取html中所有的链接
1.搜索到所有的a标签
2.解析a标签格式,提取href后的链接内容
>>> import requests
>>> r = requests.get(url='http://www.baidu.com',timeout=30)
>>> r.status_code
200
>>> r.encoding=r.apparent_encoding
>>> demo = r.text
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(demo,'html.parser')
>>> for i in soup.find_all('a'): #find_all()方法获取页面中所有的a标签
print(i.get('href')) #get()方法获取url链接
http://news.baidu.com
http://www.hao123.com
http://map.baidu.com
http://v.baidu.com
http://tieba.baidu.com
BeautifulSoup库提供了一个方法
find_all(name,attrs,recursive,string,**kwargs)
参数都是精确搜索,需要利用正则表达式模糊索索
import re
re.compilt()
这个方法可以在soup的变量中查找信息,返回的是列表类型,存储查找的结果
name:对标签名称的检索字符串
>>> for i in soup.find_all(True): #true则返回所有标签
print(i.name)
html
head
meta
meta
meta
link
title
body
>>> soup.find_all(['a','b']) #返回a、b标签,同一个列表中
返回以b开头的标签:需要利用正则表达式库 re
>>> for i in soup.find_all(re.compile('b')): #返回以b开头的标签,模糊匹配
print(i.name)
body
attrs:对标签属性值的检索字符串,可以标注属性检索,属性值需要属性名称='属性值'
>>> soup.find_all('p',target='_blank') #查找p标签,而且属性值是_blank的
[]
>>> soup.find_all(id='su') #查找id属性值是su的标签
[]
>>> soup.find_all(re.compile('link')) #查找标签包含link的标签
[百度一下,你就知道 ]
>>> soup.find_all(id=re.compile('link')) #查找属性名为id,值为包含link的标签
[]
recursive:是否对子孙全部检索,默认是True>>> soup.find_all('a',recursive=False) #将recursive设为False则只在儿子节点检索
[]
>>> soup.find_all(string='登录') #string检索
['登录']
>>> soup.find_all(string='关于') #字符串检索,必须精确的检索,通过正则表达式可以模糊索索
[]
>>> soup.find_all(string=re.compile('关于')) #通过正则表达式和find_all()函数可以很有效的通过字符串检索查该html或xml页面所有待该页面的该字段
['关于百度']
find_all()函数的简写形式:
soup()====soup.find_all()
需求:
输入:需要爬取的URL
输出:需要爬取到的信息,并按一定排序输出
方法:requests、BeautifulSoup
定向爬虫:是指仅对指定的URL进行爬取,不扩展爬取
这类爬虫只能获取静态页面信息,对于js动态生成的页面信息再说。通过源代码可以判断是否是js动态生成。
程序设计思路:
1.从网页上获取网页内容---requests
2.提取网页内容的有效信息,并把信息放进合适的数据结构中,这样可以将信息变成我们代码的一部分
3.利用数据结构输出其中的信息,并达到我们所要的数据结果
程序结构设计:
1.获取网页内容
getHTMLText()
2.获取网络内容信息并存储到合适的数据结构中,列表
fillUnivList()
3.利用数据结构展示并输出结果
printUnivList()
实现代码:
import requests
from bs4 import BeautifulSoup
import bs4
#获取html页面
def getHTMLText(url):
try:
r = requests.get(url,timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
#将html页面放到list列表中
def fillUnivList(ulist,html):
soup = BeautifulSoup(html,'html.parser')
# 查找tbody标签然后,下行遍历,只遍历儿子节点(tr)。tr标签是行,这里是一所大学的信息。
for tr in soup.find("tbody").children:
#tbody的儿子节点可能有tr标签和字符串类型(字符串类型也是节点),这里所有信息都在tr标签中,这里需要过滤掉非标签类型的信息
#这里需要import bs4,检测循环遍历tr的类型,若不是bs4库定义的标签类型则过滤掉
if isinstance(tr,bs4.element.Tag):
#对tr标签中的td标签做查询,存为列表类型tds
#tds = tr.find_all("td")
tds = tr('td') #find_all()的简写
#将排名、大学名称、大学排分加进ulist列表中
ulist.append([tds[0].string,tds[1].string,tds[3].string])
#将列表信息打印出来,num表示将html页面的多少个学校打印出来
def printUnivList(ulist,num):
#.format()方法格式化输出
#表头的打印
print("{:^10}\t{:^6}\t{:^10}".format("排名","学校名称","总分"))
#大学信息打印,格式和表头一致
for i in range(num):
u = ulist[i]
print("{:^10}\t{:^6}\t{:^10}".format(u[0],u[1],u[2]))
print("Suc" + str(num))
#主函数
def main():
#将大学信息放进ulist列表中
ulist = []
url = "http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html"
#调用3个步骤对应的函数
#将URL转换为html
html = getHTMLText(url)
#将html信息提取后放在uinfo的变量中
fillUnivList(ulist,html)
#打印大学信息
printUnivList(ulist,25) #这里选取20所学校信息
main()
.format输出中文字符和西文字符
中文输出对齐问题在输出是中英文混合的情况下都存在。
解决方案:
当中文字符宽度不够时,系统默认采用西文字符填充。只要采用中文字符空格填充而不用西文空格填充,中文对齐问题就很很好解决了。
采用中文字符的空格填充采用chr(12288)表示一个中文空格。
#将列表信息打印出来
def printUnivList(ulist,num):
tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
print(tplt.format("排名","学校名称","总分",chr(12288)))
for i in range(num):
u = ulist[i]
print(tplt.format(u[0],u[1],u[2],chr(12288)))
print("Suc" + str(num))
另外正则表达式在文本处理中十分常用:
正则表达式是由字符和操作符构成的。
正则表达式的常用操作符
常规例子:
P(Y|YT|YTH|YTHO)?N "PN","PYN","PYTN","PYTHN","PYTHON"
PYTHON+ "PYTHON","PYTHONN","PYTHONNN..."
PY[TH]ON "PYTON","PYHON"
PY[^TH]?ON "PYON","PYAON","PYXON"
PY{:3}N "PN","PYN","PYYN","PYYYN"
经典例子:
^[A-Za-z]+$ 由26个字母组成的字符串
^[A-Za-z0-9]+$ 由26个字母和数字组成的字符串
^-?\d+$ 整数形式的字符串
^[0-9]*[1-9][0-9]*$ 正整数形式的字符串
[1-9]\d{5} 中国境内邮政编码,6位
[\u4e00-\u9fa5] 匹配中文字符 #采用utf8编码约定中文字符取值范围
\d{3}-\d{8}|\d{4}-\d{7} 国内电话号码,010-68913536 11位啊
由字符串来构造正则表达式:
0-99: [1-9]?\d
100-199:1\d{2}
200-249:2[0-4]\d
250-255:25[0-5]
ip地址:
(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]).){3}([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])
re库是Python的的标准库,主要用于字符串匹配。
调用方式:
import re
re库采用raw string类型,也就是原生字符串类型。
原生字符串类型是指:斜杠\ 不会转意。因为正则表达式中操作符有些是和斜杠一起构成的,使用原生字符串就可以防止转意了。
在字符串前加一个小写字母r就可以了。
如:
r"[1-9]?\d"
所以:
当正则表达式中包含转意符号时,使用原生字符串。
函数的具体说明:
re.search(pattern,string,flags=0)
作用:
在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象。
参数说明:
flags:正则表达式使用时的控制标记
例子:
>>> import re #导入re库
>>> match = re.search(r'[1-9]\d{5}','GHF345326 GHF525300') #匹配邮政编码,返回的是一个match对象
>>> if match:
print(match.group(0))
345326
re.match(pattern,string,flags=0)从一个字符串的开始位置起匹配正则表达式,返回match对象。
参数:
同search
例子:
>>> import re
>>> match1 = re.match(r'[1-9]\d{5}','GHF535343')
>>> if match1:
print(match1.group(0))
#match()方法是从字符串的起始位置开始匹配,开始字符串不是,所有match对象为空。
>>> match1.group(0) #要对返回的match对象进行打印时,需要判断match对象是否为空,不为空才能调用group(0)方法。
Traceback (most recent call last):
File "", line 1, in
match1.group(0)
AttributeError: 'NoneType' object has no attribute 'group'
>>> match2 = re.match(r'[1-9]\d{5}','525300 GHT')
>>> if match2:
match2.group(0)
'525300'
re.findall(pattern,string,flags=0)
搜索字符串,以列表的形式返回所有能够匹配的子串。
参数同search
例子:
>>> import re
>>> ls =re.findall(r'[1-9]\d{5}','234323gg GHT838476ff535243')
>>> ls
['234323', '838476', '535243']
re.split(patttern,string,maxsplit=0,flags=0)
将一个字符串按照正则表达式匹配的结果进行分割,返回列表类型。
pattren\string\flags和search、match、findall方法一样,
maxsplit:最大分割数,剩余部分作为最后一个元素输出
例子:
>>> import re
>>> ls = re.split(r'[1-9]\d{5}','FG123456JJJ213456 234567SDF')
>>> ls
['FG', 'JJJ', ' ', 'SDF'] #返回一个列表
>>> ls1 = re.split(r'[1-9]\d{5}','FJD324538HJH879045',maxsplit=1) #最大分割是1,所有只分割1个,剩下的原路返回
>>> ls1
['FJD', 'HJH879045']
>>>
re.finditer(pattern,string,flags=0)搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素都是match对象。
参数和search、match、findall方法一样
例子:
>>> import re
>>> srting = 'ere123456kij234567pl345435'
>>> ls = re.finditer(r'[1-9]\d{5}',srting)
>>> type(ls)
>>> for i in ls:
if i :
print(i.group(0))
123456
234567
345435
re.sub(pattern,repl,string,count=0,flags=0)
在一个字符串中替换所有匹配正则表达式的子串,返回被替换后的字符串。
参数pattern、string、flags和search、match、findall的参数一样。
repl:替换匹配字符串的字符串
count:匹配的最大替换次数
例子:
>>> import re
>>> re.sub(r'[1-9]\d{5}','repl111','GHT212123GTY345673',count=2)
'GHTrepl111GTYrepl111'
>>> re.sub(r'[1-9]\d{5}','+test+','GTH787878GHU898789')
'GTH+test+GHU+test+'
>>> re.sub(r'[1-9]\d{5}','+test+','GTH787878GHU898789',count=1)
'GTH+test+GHU898789'
>>>
以上通过.调用的方法使用re的方法是函数式用法:一次性操作
正则表达式还有另外一种方法:
面向对象用法:编译后多次操作
>>> import re
>>> pat = re.compile(r'[1-9]\d{5}')
>>> rst = pat.search('GHT 525343')
>>> rst
<_sre.SRE_Match object; span=(4, 10), match='525343'>
>>> if rst:
print(rst.group(0))
525343
先使用compile()编译成正则表达式,之后再调用search、match、findall、split、sub、finditer方法。
没经过compile的字符串只是正则表达式的一种表现形式,经过compile后的才是正则表达式。
优点:一次编译可以多次使用该正则表达式进行匹配。
pat = compile(pattern,flags=0)
作用:
将一个正则表达式编译成一个正则表达式对象。
参数说明:
pattern:正则表达式字符串或原生字符串;
flags:正则表达式使用时的控制标记;
re的search()、match()、finditer()返回的是一个match对象,search、match只返回匹配到的第一个字符串,需要返回全部匹配的字符串使用finditer,for循环全部打印出来。
match对象是:一次匹配的结果,它包含了很多匹配的相关信息。
match对象的属性
>>> import re
>>> i = re.search(r'[1-9]\d{5}','GTR343435')
>>> if i :
print(i.group(0))
343435
>>> i.string #待匹配的文本
'GTR343435'
>>> i.re #正则表达式
re.compile('[1-9]\\d{5}')
>>> i.pos #搜索文本的开始位置
0
>>> i.endpos #搜索文本的结束位置
9
match对象的方法
>>> i.start() #匹配字符串在原字符串的开始位置
3
>>> i.end() #匹配字符串在原字符串的结束位置
9
>>> i.span() #返回一个元组,包括匹配字符串在原字符串的开始位置和结束位置
(3, 9)
>>> i.group(0) #返回匹配后的字符串
'343435'
re库默认采用贪婪匹配的方式,也就是返回匹配的最长项,如:
>>> import re
>>> i = re.search(r'py.*n','pyanbncndnfngn')
>>> if i:
i.group(0)
'pyanbncndnfngn' #默认采用贪婪匹配,返回最长的
如何输出最小匹配呢?
>>> ii = re.match(r'py.*?n','pyanbncndnfngn')
>>> if ii :
ii.group(0)
'pyan'
最小匹配操作符:
当有操作符可以匹配不同长度时,我们都可以在操作符后面增加一个问号?来获取最小匹配。
什么时候采用BeautifulSoup库?
什么时候采用re库?
什么样的网站适合定向爬虫实现爬取?数据写在html页面中,可以通过定位到元素,一般的静态页面,但那种有js生成的页面,源代码没有显示页面,不适合定向爬虫。
从众多的文本信息中提取我们需要的信息,使用正则表达式是最好的。
当不使用BeautifulSoup库进行信息提取,通过搜索的方式提取信息,使用re库是最合适的。
新知识点:
import requests
import re
import time
#获取html页面
def getHTMLText(url):
try:
r = requests.get(url,timeout = 30)
r.raise_for_status()
r.encoding = "utf-8" #可以节约时间
return r.text
except:
return ""
#对获取的每一个页面进行解析,ilt是结果的列表类型
def parsePage(ilt,html):
try:
#"view_price":"149.00"
#反斜杠\表示引入双引号,获取价格信息保存到plt中
plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html)
#"raw_title":"2017春季新款双肩包女韩版时尚pu背包流苏子母包百搭学院风书包"
#*?是最小匹配
tlt = re.findall(r'\"raw_title\"\:\".*?\"',html)
#获取了商品的价格和信息,下面对这两个信息关联起来,保存到我们要输出的变量中
#plt\tlt列表长度一样,元素位置一一对应的,同一个位置的元素是同一个商品的
for i in range(len(plt)):
#字符串分割获取商品的价格,eval()函数能够将字符串的最外层的双引号或单引号去掉
price = eval(plt[i].split(':')[1])
#获得商品名称
title = eval(tlt[i].split(":")[1])
#将信息需要输出的列表中
ilt.append([price,title])
except:
print("")
#将解析后的信息输出
def printGoodsList(ilt):
#先设计一个打印模板tplt,希望打印什么格式,
# {}定义槽函数,{:4}第一个位置长度为4,中间8,最后是16
tplt = "{:4}\t{:8}\t{:16}"
#打印输出信息的表头
print(tplt.format("序号","价格","商品名称"))
#定义一个输出信息计数器,商品序号
count = 0
#对所有的信息进行输出显示
for i in ilt:
count +=1 #序号,价格,名称
print(tplt.format(count,i[0],i[1]))
#定义主函数,记录整个程序运行的过程
def main():
start_time = time.time()
#搜索关键词goods
goods = "书包"
#设定向下一页爬取的深度,爬取页数depth
depth = 2
#爬取的URL
start_url = "https://s.taobao.com/search?q=" + goods
#定义变量infoList 表示输出结果
infoList = []
#因为每一个页面URL不同,需要对每一个页面进行单独访问和处理
for i in range(depth):
try: #使用try多获取页面进行异常判断,如果某页面解析出问题,可以跳过该页面,往下继续,不会造成出现解析错误,程序停止
#对每一个页面的URL链接进行设计,因为淘宝每个页面显示44个商品
url = start_url + '&s=' + str(44*i)
html = getHTMLText(url) #获取页面内容
parsePage(infoList,html) #对获取的页面进行处理
except:
#continue语句只是结束本次循环,而不会终止循环的执行。break语句则是终止整个循环过程
continue
#将获取的页面信息输出
printGoodsList(infoList)
end_time = time.time()
print(end_time-start_time)
print(round(end_time-start_time))
main()
采用技术路线:
BeautifulSoup + re + requests + traceback(方便调试)
注意的知识点:
share_reptile.py
import requests
import re
import traceback
from bs4 import BeautifulSoup
#获得URL对应的页面
def getHTMLText(url):
try:
r = requests.get(url,timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
#获得股票的信息列表,lst是保存所有股票的列表,存了所有股票信息;
# stockURL是获得股票列表的URL网站
def getStockList(lst,stockURL):
html = getHTMLText(stockURL) #获取东方财富网的html
soup = BeautifulSoup(html,"html.parser")
a = soup.find_all('a') #通过find_all()获得页面的a标签,a是一个列表
for i in a :
try: #代码中有很多不是股票链接的a标签的,解析可能出现错误,需要采用try...except...框架
href = i.attrs["href"] #以s开头中间是h或z后面是6个数字的正则
lst.append(re.findall(r'[s][hz]\d{6}',href)[0])#可以找到所有的股票URL
except:
continue
#获得每一只个股的股票信息,lst是保存所有股票的列表;stockURL是获得股票列表的URL网站;股票信息保存的文件路径
def getStockInfo(lst,stockURL,fpath):
for i in lst:
url = stockURL + i + ".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
value = valueList[i].text
infoDict[key] = value
with open(fpath,'a',encoding='utf-8') as f:
f.write(str(infoDict) + '\n')
except:
traceback.print_exc()
continue
#主函数
def main():
stock_list_url = 'http://quote.eastmoney.com/stocklist.html' #获得股票列表
stock_info_url = 'https://gupiao.baidu.com/stock/' #获取股票信息的链接的主题部分
output_file = r'D:\gupaio_reptile.txt' #保存的路径
slist = [] #股票信息,列表
getStockList(slist,stock_list_url) #获得股票列表
getStockInfo(slist,stock_info_url,output_file) #获得每一只股票信息,保存到本地
main()
在command命令行输入:pip install scrapy