第三章主要是介绍并使用了一些写爬虫过程中使用到的基本库。
最基础的HTTP库有urllib、httplib2、requests、treq等。
接下来我们就学习一下几个常用的库并在最后通过一个实例来应用。
urllib
python3中,将urllib和urllib2统一为urllib。
urllib是python内置的HTTP请求库,包含如下四个模块:
- request: 最基本的HTTP请求模块,用来模拟发送请求
- error:异常处理模块
- parse:工具模块,提供了许多URL处理方法,比如拆分、解析、合并等
- robotparser:识别网站的robots.txt文件,然后判断哪些网站可以爬,哪些不能爬,用的不多
下面重点介绍前三个模块发送请求
urlopen()
urllib.request模块提供了最基本的构造HTTP请求的方法,除了模拟浏览器发起请求,还可以处理授权验证authenticati、重定向redirection、浏览器Cookies以及其他内容。1
2
3import urllib.request
response = urllib.request.urlopen('https://www.baidu.com/')
print(response.read().decode('utf-8'))
运行结果如下:1
2
3
4
5
6
7
8
9
10<html>
<head>
<script>
location.replace(location.href.replace("https://","http://"));
</script>
</head>
<body>
<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>
用type(response)可以得到返回的是1
2
3
4
下面看一下urlopen()的API
```python
urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,capath=None,cadefault=False, context=None)
- data:可选参数,如果要使用该参数,要用bytes()方法将参数转化为字节流编码格式的内容,即bytes类型;若使用了该参数,那么请求方式是POST方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import urllib.request
import urllib.parse
data = bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf-8')
response = urllib.request.urlopen('https://httpbin.org/post',data=data)
print(response.read())
# {
# "args": {},
# "data": "",
# "files": {},
# "form": {
# "word": "hello"
# },
# "headers": {
# "Accept-Encoding": "identity",
# "Connection": "close",
# "Content-Length": "10",
# "Content-Type": "application/x-www-form-urlencoded",
# "Host": "httpbin.org",
# "User-Agent": "Python-urllib/3.6"
# },
# "json": null,
# "origin": "210.12.101.154",
# "url": "https://httpbin.org/post"
# }
传递一个参数word,其值为hello,它需要被转化成byte类型。采用bytes()方法进行转换,该方法的第一个参数是str类型,用urllib.parse模块的urlencode()方法将参数字典转化成字符串;第二个参数是指定编码格式。
我们请求的站点可以提供HTTP请求测试,用来测试POST请求,可以输出我们的请求信息,其中就有我们传递的data参数。
data参数出现在form中,表明是模拟了表单提交的方式,以POST方式传输数据。
timeout:用于设置超时时间,单位为秒,如果请求超出设置时间还没得到响应,就会抛出异常。若不指定该参数,则使用全局默认时间。
1
2
3
4
5
6
7
8import urllib.request
import socket
import urllib.error
try:
response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:
if isinstance(e.reason,socket.timeout):#判断异常是socket.timout(超时异常)
print('timeout')context:指定SSL设置,必须是ssl.SSLContext类型
- cafile:指定CA证书,请求HTTPS链接时有用
- capath:指定证书路径,请求HTTPS链接时有用
- cadefault:已经弃用了,默认为False
Request
urlopen()可以实现最基本请求的发起,如果请求中需要加入Header等信息,可以利用Request类来构建:1
2
3
4
5
6
7
8
9
10
11
12
13
14import urllib.request
request = urllib.request.Request('https://www.baidu.com')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))
# <html>
# <head>
# <script>
# location.replace(location.href.replace("https://","http://"));
# </script>
# </head>
# <body>
# <noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
# </body>
# </html>
从上述代码可以看出,urlopen的参数是一个Request类型的对象。通过构造这个数据结构,一方面可以将请求独立成一个对象,另一方面可以丰富和灵活地配置参数。
下面我们来看一下Request的构造方法:1
2class urillib.request.Request(self, url, data=None, headers={}, origin_req_host=None, unverifiabl
e=False, method=None)
- url:必传参数,请求URL
- data:必须传bytes类型的,如果是字典,可以用urllib.parse.urlencode()进行编码
headers:是一个字典,就是请求头,构造请求时通过headers参数直接构造,也可以通过调用请求实例的add_header()方法添加
添加请求头最常用的方法是修改User-Agent来伪装浏览器,默认的是Python-urllib。例如,伪装成火狐,可以修改为:
Mozilla/5.0 (X11;U;Linux i868) Gecko/20071127 Firefox/2.0.0.11origin_req_host:指请求方的host名称或ip地址
- unverifiable:表示这个请求是否是无法认证的,true表示用户没有权限来选择接收这个请求的结果。默认为False。
- method:str,指示请求使用的方法,GET/POST/PUT等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33import urllib.request
import urllib.parse
url = 'http://httpbin.org/post'
headers = {
'User-Agent':'Mozilla/4.0(compatible;MISE 5.5;Windows NT)',
'Host':'httpbin.org'
}
dict = {
'name':'Germey'
}
data = bytes(urllib.parse.urlencode(dict),encoding='utf-8')
req = urllib.request.Request(url,data=data,headers = headers,method='POST')
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))
#{
# "args": {},
# "data": "",
# "files": {},
# "form": {
# "name": "Germey"
# },
# "headers": {
# "Accept-Encoding": "identity",
# "Connection": "close",
# "Content-Length": "11",
# "Content-Type": "application/x-www-form-urlencoded",
# "Host": "httpbin.org",
# "User-Agent": "Mozilla/4.0(compatible;MISE 5.5;Windows NT)"
# },
# "json": null,
# "origin": "210.12.101.154",
# "url": "http://httpbin.org/post"
# }
可以看到,我们成功设置了data、method、headers
另外,之前我们提到headers可以用add_headers()来添加:1
2req = request.Request(url,data,method='POST')
req.add_header('User-Agent','Mozilla/4.0(compatible;MISE 5.5;Windows NT)')
高级用法
Handler,可以认为是各种处理器,比如处理登录验证、处理Cookies、处理代理设置。
urllib.request.BaseHandler类,是所有其他Handler的父类,提供了最基本的方法,然后各种Handler子类继承该类。
下面具体看一下常用的几个用法:
1.验证
有时候我们会遇到图中情况,提示输入用户名和密码,验证成功后才能查看页面
如果请求这种页面,借助HTTPBasicAuthHandler来处理:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener
from urllib.request import URLError
username = 'username'
password = 'password'
url = 'http://localhost:5000/'
p = HTTPPasswordMgrWithDefaultRealm() #创建一个密码管理对象
#添加账户信息,第一个参数realm是与远程服务器相关的域信息,一般写None,后面三个参数分别是 Web服务器、用户名、密码
p.add_password(None,url,username,password)
#构建HTTPBasicAuthHandler对象,参数是创建的密码管理对象
auth_handler = HTTPBasicAuthHandler(p)
#创建自定义opener对象
opener = build_opener(auth_handler) #urlopen就是一个Opener,配置Opener可以实现更高级的功能
try:
result = opener.open(url)
html = result.read().decode('utf-8')
print(html)
except URLError as e:
print(e.reason)
实例化HTTPBasicAuthHandler对象,其参数是HTTPPasswordMgrWithDefaultRealm对象,利用add_password()添加用户名和密码,这样就建立了一个处理验证的Handler。
2.代理
做爬虫时添加代理:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from urllib.error import URLError
from urllib.request import ProxyHandler,build_opener
proxy_handler = ProxyHandler({
#代理列表,键名是协议类型,value是代理连接,可以添加多个代理
'http':'http://127.0.0.1:9743',#在本地搭建代理,运行在9743端口
'https':'https://127.0.0.1:9743'
})
opener = build_opener(proxy_handler)
#构造完代理handler后,发送请求
try:
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))
except URLError as e:
print(e.reason)
3.Cookies
首先看一下如何获取网站的Cookies:1
2
3
4
5
6
7
8import http.cookiejar,urllib.request
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
for item in cookie:
print(item.name+'='+item.value)
会输出每条Cookie的名称和值。
有时候,需要我们将Cookies存为文件:1
2
3
4
5
6
7import http.cookiejar,urllib.request
filename = 'cookies.txt'
cookie = http.cookiejar.MozillaCookieJar(filename)
handle = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handle)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True,ignore_expires=True)
MozillaCookieJar是CookieJar的子类,可以用来处理Cookies和文件相关的事件,比如读取和保存Cookies,可以将Cookies保存成Mozilla型浏览器的Cookies格式。
另外,LWPCookieJar也是类似的,保存的事libwww-perl(LWP)格式的Cookies文件。
下面看一下读取cookies文件的操作:1
2
3
4
5
6
7import http.cookiejar,urllib.request
cookie = http.cookiejar.MozillaCookieJar()
cookie.load('cookies.txt',ignore_discard=True,ignore_expires = True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))
上面出现过的两个参数ignore_discard、ignore_expires,ignore_discard的意思是即使cookies将被丢弃也将它保存下来,ignore_expires的意思是如果cookies已经过期也将它保存并且文件已存在时将覆盖,在这里,我们将这两个全部设置为True。
处理异常
发送请求时,如果遇到异常不处理,程序可能会报错停止运行,所以有必要进行异常处理。
urllib的error模块定义了由request模块产生的异常。
URLError
由request模块产生的异常都可以通过捕获这个类来处理:1
2
3
4
5
6from urllib import request,error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.URLError as e:
print(e.reason)
#Not Found
HTTPError
它是URLError的子类,专门处理HTTP请求错误,比如认证失败等,有code、reason、headers三个属性。
- code: 返回HTTP状态码
- reason:返回错误原因
- headers:返回请求头
1
2
3
4
5from urllib import request,error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason,e.code,e.headers,sep='\n')
因为HTTPError是URLError的子类,所以我们一般异常处理时都是先捕获子类异常,再是父类错误,所以优化后的代码如下:1
2
3
4
5
6
7
8
9from urllib import request,error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason,e.code,e.headers,sep='\n')
except error.URLError as e:
print(e.reason)
else:
print('Request Successfully')
有时候,reason属性返回的不一定是字符串,可能是一个对象:1
2
3
4
5
6
7
8
9
10
11import socket
from urllib import request,error
try:
response = request.urlopen('https://www.baidu.com',timeout=0.01)
except error.URLError as e:
print(type(e.reason))
if isinstance(e.reason,socket.timeout):
print('TIME OUT')
# <class 'socket.timeout'>
# TIME OUT
解析链接
1.urlparse()
from urllib.parse import urlparse,可以实现URL的识别和分段。1
2
3
4urllib.parse.urlparse(urlstring,scheme='',allow_fragments=True)
# http://www.baidu.com/index.html;user?id=5#comment
# scheme:http ; netloc:www.baidu.com ; path:index.html ;
# params:user ; query: id=5 ; fragment:comment.
scheme是默认协议,如果url没有带协议信息,将scheme作为默认协议;
allow_fragments默认为True,是否忽略fragment;如果被设置为false,则忽略fragment部分,该部分被解析为path/parameters/query的一部分。
另外,返回结果是一个元组,可以用索引顺序来获取,也可以用属性名获取。
2.urlunparse()
长度必须是61
2
3
4from urllib.parse import urlparse
result = urlparse('www.baidu.com/index.html;user?id=5#comment',scheme='https')
print(result)
#http://www.baidu.com/index.html;user?id=5#comment
3.urlsplit()
和urlparse类似,只是不解析param部分1
2
3
4
5
6
7from urllib.parse import urlsplit
result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment')
print(result)
#SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')
print(result.scheme,result[0],sep='\n')
#http
#http
4.urlunsplit()
与urlunparse类似,将链接的各个部分组合成完整链接。传入对象也是可迭代的,比如元组、列表等,唯一区别是长度为61
2
3
4from urllib.parse import urlunsplit
data = ['http','www.baidu.com','index.html','a=6','comment']
print(urlunsplit(data))
#http://www.baidu.com/index.html?a=6#comment
5.urljoin()
也是一种生成连接的方法,基础链接base_url作为第一个参数,新的链接作为第二个参数,该方法分析base_url的scheme、netloc、path,并对新链接缺失部分进行填充,返回结果。1
2
3
4
5
6
7
8
9
10
11
12from urllib.parse import urljoin
print(urljoin('http://www.baidu.com','FAQ.html'))
print(urljoin('http://www.baidu.com/about.html','https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com?wd=abc','https://cuiqingcai.com/index.php'))
print(urljoin('www.baidu.com','?category=2#comment'))
print(urljoin('www.baidu.com#comment','?category=2'))
# http://www.baidu.com/index.html?a=6#comment
# http://www.baidu.com/FAQ.html
# https://cuiqingcai.com/FAQ.html
# https://cuiqingcai.com/index.php
# www.baidu.com?category=2#comment
# www.baidu.com?category=2
如果新的链接也就是第二个参数不存在scheme、netloc、path,则用base_url中的scheme、netloc、path进行填充,简单的说就是以新的链接为主,如果缺少什么,就从base_url中进行补充。
6.urlencode()
在构造GET请求时非常有效1
2
3
4
5from urllib.parse import urlencode
params = {'name':'Alice','age':'1'}
base_url = 'http://www.baidu.com?'
print(base_url+urlencode(params))
#http://www.baidu.com?name=Alice&age=1
首先用字典将参数表示出来,然后用urlencode()将其序列化为GET请求参数
7.parse_qs()1
2
3
4from urllib.parse import parse_qs
query = 'name=Alice&age=1'
print(parse_qs(query))
#{'name': ['Alice'], 'age': ['1']}
这是一个反序列化方法,将GET请求转回字典类型。
8.parse_qsl()1
2
3
4from urllib.parse import parse_qsl
query = 'name=Alice&age=1'
print(parse_qsl(query))
#[('name', 'Alice'), ('age', '1')]
将参数转化为元组组成的列表
9.quote()1
2
3
4
5from urllib.parse import quote
keyword = '壁纸'
url = 'http://www.baidu.com/s?wd='+quote(keyword)
print(url)
#http://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8
这方法是将内容转化为URL编码的格式,将中文转化为URL编码。
10.unquote()
就是URL解码1
2
3
4from urllib.parse import unquote
url = 'http://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8'
print(unquote(url))
#http://www.baidu.com/s?wd=壁纸
分析Robots协议
首先我们来了解一下什么是Robots协议。
该协议也被称为爬虫协议、机器人协议,全名叫做网络爬虫排除标准
一般是一个txt文本,放在网站根目录下和王赞的入口文件网在一起,用来告诉爬虫和搜索引擎哪些引擎可以抓取,那些不可以抓取。1
2
3User-agent: *
Disallow: /
Allow: /public/
上面这个robots.txt只允许爬虫爬取public目录。
USer-agent设置为*代表该协议对任何爬取爬虫有效。当设置为User-agent: Baiduspider则代表我们设置的规则只对百度爬虫有效。
禁止所有爬虫爬取任何目录:1
2User-agent: *
Disallow: /
允许所有爬虫爬取任何目录:1
2User-agent: *
Disallow:
只允许某个爬虫访问:1
2
3
4User-agent: WebCrawler
Disallow:
User-agent: *
Disallow: /
这时我们就有一个疑问,爬虫名是如何来的?很多网站都有固定的爬虫名字,可以搜索看看。
了解完robots.txt后, 可以用robotparser模块来解析了。该模块提供了一个类RobotFileParser,可以根据某网站的robots.txt来判断一个爬取爬虫是否有权限来爬取这个网页。1
2
3
4
5
6
7
8from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url('http://www.jianshu.com/robots.txt')
rp.read()
a = rp.can_fetch('*','http://www.jianshu.com/p/b67554025d7d')
b = rp.can_fetch('*','http://www.jianshu.com/search?q=python&page=1&type=collections')
print(a,b)
#True False
set_url()就是设置robots.txt文件链接,如果在创建对象时就已经传入了链接,则不需要该方法。
read()读取robots.txt并进行分析,如果不调用该方法,后续判断都为false,所以一定要调用,但他不会返回任何东西。
can_fetch()第一个参数是User-agent,第二个参数是要抓取的URL判断该搜索引擎是否可以抓取这个URL,返回true or false。
mtime()返回上次抓取分析robots.txt的时间。
modified()将当前时间设置为上次抓取和分析robots.txt的时间。
parse()解析robots.txt,传入的参数是robots.txt的某些行的内容,然后按照robots.txt的语法规则来分析这些内容。1
2
3
4
5
6
7
8
9
10
11
12
13
14from urllib.robotparser import RobotFileParser
from urllib.request import urlopen
from urllib.request import Request
rp = RobotFileParser()
# 如果不加上下面的这行出现会出现urllib.error.HTTPError: HTTP Error 403: Forbidden错误
# 主要是由于该网站禁止爬虫导致的,可以在请求加上头信息,伪装成浏览器访问User-Agent
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
req = Request('http://www.jianshu.com/robots.txt',headers = headers)
rp.parse(urlopen(req).read().decode('utf-8').split('\n'))
a = rp.can_fetch('*','http://www.jianshu.com/p/b67554025d7d')
b = rp.can_fetch('*','http://www.jianshu.com/search?q=python&page=1&type=collections')
print(a,b)
#True False
requests
基本用法
GET请求
发起GET请求,并添加额外信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import requests
data = {'name':'Alice','age':'1'}
r = requests.get('http://httpbin.org/get',params=data)
print(r.text)
# {
# "args": {
# "age": "1",
# "name": "Alice"
# },
# "headers": {
# "Accept": "*/*",
# "Accept-Encoding": "gzip, deflate",
# "Connection": "close",
# "Host": "httpbin.org",
# "User-Agent": "python-requests/2.18.4"
# },
# "origin": "210.12.101.154",
# "url": "http://httpbin.org/get?name=Alice&age=1"
# }
抓取网页:
这里是结合了正则表达式来匹配所有的问题内容。1
2
3
4
5
6
7
8
9import requests
import re
#加上浏览器标志信息,否则知乎禁止抓取
headers = {'User-Agent':'Mozilla/5.0(Macintosh;Inter Mac OS X 10_11_4)AppleWebkit/537.36(KHTML,like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
r = requests.get('http://www.zhihu.com/explore',headers=headers)
pattern = re.compile('explore-feed.*?question_link.*?>(.*?)</a>',re.S)
titles = re.findall(pattern,r.text)
print(titles)
#['\n有哪些知识是你学医之后才知道的?\n', '\nNature 最新研究发现彩色蛋最先由恐龙孵化出来,这揭示了什么?\n', '\n如何看待刘强东告诉明尼苏达性侵案受害者「你可以成为邓文迪那样的女人」?\n', '\n有哪些化学知识概念已经更新或改变,但不为大众所知?\n', '\n抑郁症患者的自杀是毫无征兆的吗?\n', '\n余沧海屠戮林家,方证、冲虚这些德高望重的人为何不替林平之主持公道,还在任我行攻击余沧海的时候舍身保护?\n', '\n影视作品里有哪些不为人知的情节,或者是一些细思极恐,有趣的部分?\n', '\n如何评价布鲁克林 · 贝克汉姆在 instagram 上发布涉嫌种族歧视的内容?\n', '\n得知越来越多的女大学生被包养,已有的价值观开始动摇怎么办?\n', '\n如果是诺夫哥罗德而非莫斯科统一了俄罗斯会怎样?\n']
抓取二进制数据:
图片、音频、视频本质上都是二进制码组成的。如果想要抓取它们,就要得到它们的二进制码。
下面这段就是抓取github的图标并保存1
2
3
4import requests
r = requests.get('https://github.com/favicon.ico')
with open('favicon.ico','wb') as f:
f.write(r.content)
添加headers:
通过headers参数来传达头信息,否则不能正常请求网站。
POST请求
1 | import requests |
form部分就是我们提交的数据,证明POST请求成功了。
响应
status_code:状态相应码
headers:响应头
cookies:得到Cookies
url:URL
history:请求历史
requests还提供了很多状态内置码
例如:判断结果是不是404,可以用r.status_code == resquests.codes.not_found判断【r = requests.get(‘url’)】
高级用法
文件上传
上传一个favicon.ico文件1
2
3
4
5import requests
files = {'files':open('favicon.ico','rb')}
#files = [('files',open('favicon.ico','rb'))]
r = requests.post('http://httpbin.org/post',files=files)
print(r.text)
files可以是字典,也可以是元组。
Cookies
1 | import requests |
用Cookies维持登录状态:1
2
3
4
5
6import requests
headers = {'Cookie':手动打码,
'Host':'www.zhihu.com',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'}
r = requests.get('http://www.zhihu.com',headers=headers)
print(r.text)
会话维持
在requests中,get和post请求网页实际上是不同的会话,相当于用两个浏览器打开了不同的页面。例如,先用post登陆了某网站,然后用get方法请求个人信息,实际上这相当于打开了两个浏览器,是完全不相关的两个会话,不能获取个人信息。
可以设置相同的cookies,也可以通过下面的方法维持会话:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import requests
#设置cookies
requests.get('http://httpbin.org/cookies/set/number/123456')
r = requests.get('http://httpbin.org/cookies')
print(r.text)
# {
# "cookies": {}
# }
#利用Session维持会话
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456')
r = s.get('http://httpbin.org/cookies')
print(r.text)
# {
# "cookies": {
# "number": "123456"
# }
# }
SSL证书验证
发送http请求时,要验证SSL证书,遇到证书不被官方CA机构信任的网站,只要把verify改为false即可
requests.get(‘url’,verify = False)
遇到建议给出证书的警告时,可以用下面两种方法:1
2
3
4
5from requests.packages import urllib3
urllib3.disable_warnings()
import logging
logging.captureWarnings(True)
当然,也可以使用本地证书,但要注意key是解密状态的。
代理设置
1 | import requests |
超时设置
设置超时时间,若超过还没得到响应,则报错。该时间为发出请求到服务器返回响应的时间。1
2
3import requests
r = requests.get('http://www.taobao.com',timeout=1)
print(r.status_code)
timeout默认为None,意味着永久等待。
身份认证
1
2import requests
r = requests.get('url',auth=('username','password'))
Prepared Request
将请求表达为数据结构:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from requests import Request,Session
url = 'http://httpbin.org/post'
data = {
'name':'Alice',
'age':1
}
headers = {
'User-Agent':'Mozilla/5.0(Macintosh;Inter Mac OS X 10_11_4)AppleWebkit/537.36(KHTML,like Gecko)Chrome/52.0.2743.116 Safari/537.36'
}
s = Session()
#构造Request对象
req = Request('POST',url=url,data=data,headers=headers)
#调用Session的prepared_request()方法将其转化成一个Prepared Request对象
prepped = s.prepare_request(req)
#send()发送
r = s.send(prepped)
print(r.text)
正则表达式
python的re库提供了整个正则表达式的实现。
match()
传入要匹配的字符串和正则表达式,就可以检测是否匹配
注意,它是从字符串的开头开始匹配的,一旦开头匹配不上,则失败1
2
3
4
5
6
7
8
9
10
11
12import re
content = 'Hello 123 4567 World_this is a Regex Demo'
print(len(content))
# ^字符串的开头 \s空白字符 \S非空字符 \d数字 \w数字字母下划线 {n}前面的表达式重复n次
result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}',content)
print(result)
print(result.group())#匹配的结果
print(result.span())#匹配的范围
# 41
# <_sre.SRE_Match object; span=(0, 25), match='Hello 123 4567 World_this'>
# Hello 123 4567 World_this
# (0, 25)
匹配目标
从字符串中提取一部分内容,用()将想提取的子字符串括起来,然后用group()方法传入分组的索引剧可以获取提取的结果:1
2
3
4
5
6
7
8
9
10
11
12
13
14import re
content = 'Hello 123 4567 World_this is a Regex Demo'
print(len(content))
result = re.match('^Hello\s(\d+)\s(\d+)',content)
print(result)
print(result.group())
print(result.group(1))
print(result.group(2))
print(result.span())
# <_sre.SRE_Match object; span=(0, 14), match='Hello 123 4567'>
# Hello 123 4567
# 123
# 4567
# (0, 14)
通用匹配
.表示任意字符(除了换行符),*表示无限次
那么开头的例子可以改写成:1
2
3
4
5
6
7
8
9
10
11
12import re
content = 'Hello 123 4567 World_this is a Regex Demo'
print(len(content))
#$匹配字符串的结尾
result = re.match('^Hello.*Demo$',content)
print(result)
print(result.group())
print(result.span())
# 41
# <_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_this is a Regex Demo'>
# Hello 123 4567 World_this is a Regex Demo
# (0, 41)
贪婪与非贪婪
1 | import re |
在贪婪匹配下,.会尽可能多的匹配字符,所以\d+只有一个7
若想提取提出字符串1234567,就使用非贪婪匹配.?:1
2
3
4
5
6import re
content = 'Hello 1234567 World_this is a Regex Demo'
# ?匹配0个或1个前面定义的片段
result = re.match('^He.*?(\d+).*Demo$',content)
print(result.group(1))
#1234567
但如果.*?是在结尾,则可能匹配不到任何内容
修饰符
.?是不能匹配换行符的,如果字符串存在换行符,那么可以在re.match()中加入re.S,比如result = re.re.match(‘^He.?(\d+).*Demo$’,content,re.S)
常用的有:re.I使对大小写不敏感;re.L做本地化识别匹配;re.M多行匹配
转义匹配
遇到要转义的字符使用\
search()
弥补了match必须开头就要匹配的缺点,扫描整个字符串,返回第一个匹配的结果。
re.S可以跳过换行符,十分重要。
findall()
返回取所有匹配的字符串
sub()
修改字符串1
2
3
4
5import re
content = 'This1 i2s a d3emo4'
result = re.sub('\d+','',content)
print(result)
#This is a demo
compile()
将正则字符串编译成正则表达式对象,以便后面的匹配中使用。1
2
3
4
5
6
7
8
9
10import re
content1 = '2018-05-06 13:00'
content2 = '2018-05-09 14:00'
content3 = '2018-11-05 13:14'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern,'',content1)
result2 = re.sub(pattern,'',content2)
result3 = re.sub(pattern,'',content3)
print(result1,result2,result3)
#2018-05-06 2018-05-09 2018-11-05
实例:抓取猫眼电影排行榜
具体过程看下一篇文章😂