Python爬虫入门 —— 基本爬虫库的使用

文章目录

  • 使用urllib库
    • 使用request模块发送请求
      • 1.使用urlopen()
        • urlopen - data参数
        • urlopen - timeout参数
      • 2. Request()
      • 3.高级用法
        • 验证
        • 代理
        • Cookies
    • 使用error模块处理异常
      • 1. URLError
      • 2. HTTPError
    • 使用parse模块解析链接
      • 1. urlparse()
      • 2. urlunparse()
      • 3. urlsplit()
      • 4. urlunsplit()
      • 5. urljoin()
      • 6. urlencode()
      • 7. parse_qs()
      • 8. quote()
      • 9. unquote()
  • 使用requests库
    • 基本用法
    • GET请求
      • 抓取网页
      • 抓取二进制数据 | 图片,视频
      • 响应
    • POST请求
    • 高级用法
      • 1. 文件上传
      • 2. cookie
      • 3.会话维持
      • 4. SSL证书验证
      • 5. 代理设置
      • 6. 超时验证
      • 7. 身份认证
      • 8. Prepared Request
  • 正则表达式
    • 1. match()
      • 提取分组中的内容
      • 贪婪和非贪婪
      • 修饰符
    • 2. search()
    • 3. findall()
      • 4. sub()
      • 5. complie()

使用urllib库

  在Python2中,有urllib和urllib2两个库来实现请求的发送。而在python3中,已经不存在urllib这个库了,统一为urllib。

  首先来了解一下urllib库,它是Python内置的http请求库,所以不需要任何额外安装即可使用。它包含如下四个模块:

  • request:它是最基本的http请求模块,可以用于模拟发送请求。
  • error:异常处理模块,如果出现请求错误,我们可以捕获这些异常。
  • parse:一个工具模块,提供了许多url处理方法,比如拆分,解析,合并等。
  • robotparse:主要用于识别网站的robot.txt文件,然后判断哪些网站可以爬,哪些网站不可以爬。

使用request模块发送请求

1.使用urlopen()

# urlopen()的函数头
urllib.request.urlopen(url, data=None, [ timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
import urllib.request

response = urllib.request.urlopen('https://www.python.org')
# print(response.read().decode('utf-8'))
print(type(response))   # 输出 

  可见,response是一个HTTPResponse类型的对象,其主要包括read(),readinto(),getheader(name),getheaders(),fileno()等方法,以及msg,version,reason,debuglevel,closed等属性。

  调用read()方法可以得到目标网址的网页源码,查看status属性可以得到网站返回结果中的状态码。

import urllib.request

response = urllib.request.urlopen('https://www.python.org')
print(response.status)
print(response.getheaders())
print(response.getheader('Server'))

# /* ------------------- 输出: --------------------*\
# 200
# [
#      ('Server', 'nginx'),
#      ('Content-Type', 'text/html; charset=utf-8'),
#      ('X-Frame-Options', 'SAMEORIGIN'),
#      ('x-xss-protection', '1; mode=block'),
#      ('X-Clacks-Overhead', 'GNU Terry Pratchett'),
#      ('Via', '1.1 varnish'),
#      ('Content-Length', '49066'),
#      ('Accept-Ranges', 'bytes'),
#      ('Date', 'Thu, 17 Jan 2019 06:23:33 GMT'),
#      ('Via', '1.1 varnish'),
#      ('Age', '661'),
#      ('Connection', 'close'),
#      ('X-Served-By', 'cache-iad2134-IAD, cache-tyo19936-TYO'),
#      ('X-Cache', 'MISS, HIT'),
#      ('X-Cache-Hits', '0, 1283'),
#      ('X-Timer', 'S1547706213.363137,VS0,VE0'),
#      ('Vary', 'Cookie'),
#      ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')
#  ]
# nginx

urlopen - data参数

  data 参数是可选的,如果传递了这个参数,则请求方式就变成了POST方式。data参数必须是字节流类型的。

网站httpbin.org能将我们的请求字段再回给我们。

import urllib.request
import urllib.parse

# urlencode()将字典参数转换为字符串,bytes()将字符串转换为字节流
data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8')
response = urllib.request.urlopen('http://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.5"
#     },
#     "json": null,
#     "origin": "171.42.142.25",
#     "url": "http://httpbin.org/post"
# }

  可见,上述请求中的data数据放在了form字段中,这表明我们现在模拟的是以POST方式传输数据的表单提交方式。

urlopen - timeout参数

  timeout 参数用于设置超时时间,单位为秒。如果请求超出了设置的这个时间,还没有得到响应,那么就会抛出异常。如果不指定此参数,就使用全局默认时间。它支持HTTP , HTTPS 、FTP请求。

import urllib.request

response = urllib.request.urlopen('http://httpbin.org/get', timeout=1)
print(response.read())

# /* ------------------- 输出: --------------------*\

# {
#     "args": {},
#     "headers": {
#         "Accept-Encoding": "identity",
#         "Connection": "close",
#         "Host": "httpbin.org",
#         "User-Agent": "Python-urllib/3.5"
#     },
#     "origin": "171.42.142.25",
#     "url": "http://httpbin.org/get"
# }

由于由于请求超时会抛出异常,因此我们可以对上述代码添加异常排查:

import urllib.request
import urllib.error
import socket

try:
    response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:
    # 如果e的error类型(保存在e的reason字段中)为socket.timeout
    if isinstance(e.reason, socket.timeout):
        print('TIME OUT')

2. Request()

如果需要在请求头中添加参数,则需要Request()来构建。

import urllib.request

request = urllib.request.Request('https://python.org')
# 此时ulropen()的参数不再是一个网址了,而是一个Request类型的对象
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

Request的构造方法:

# class urllib.request.Request(url, data=None, headers={}, origin_req_host=None,unverifiable=False,method=None)
# headers:请求头,是一个字典
# origin_req_host:请求方的host名称或IP地址
# unverifiable:表示这个请求是否是无法被验证的,默认False,表明用户没有足够的权限来选择接收这个请求的结果
# method:请求的方法,如GET,POST和PUT等

下面来看一个详细的使用Request()的例子:

from urllib import request, parse

url = 'http://httpbin.org/post'
headers = {
    # 伪装游览器标识,默认的User-Agent是Python-urllib
    'User-Agent': 'Mozilla/4.0(compatible; MSIE 5.5; Windows NT)',
    # 目标主机名
    'Host':'httpbin.org'
}
# data源数据
dict = {
    'name': 'Germey'
}
data = bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=data, headers=headers, method='POST')
response = 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; MSIE 5.5; Windows NT)"
#   },
#   "json": null,
#   "origin": "171.42.142.25",
#   "url": "http://httpbin.org/post"
# }

另外,请求头除了可以直接定义外,还可以使用req的add_header()方法:

req = request.Request(url=url, data=data, method='POST')
req.add_header('User-Agent', 'Mozilla/4.0(compatible; MSIE 5.5; Windows NT)')
req.add_header('Host','httpbin.org')

3.高级用法

  在上面的使用中,虽然可以构造请求,但是对于一些更高级的操作(比如Cookies 处理、代理设置等),就需要更强大的工具Handler登场了。简而言之,我们可以把它理解为各种处理器,有专门处理登录验证的,有处理Cookies的,有处理代理设置的。利用它们,我们几乎可以做到HTTP请求中所有的事情。

  首先,介绍一下urllib.request模块中的BaseHandler类,它是所有其他Handler的父类,它提供了最基本的方法,例如default_open()、protocol_request()等。

再来看看BaseHandleer的子类:

  • HTTPDefaultErrorHandler:用于处理HTTP 响应错误,错误都会抛出HTTP Error 类型的异常。
  • HTTPRedirectHandler:用于处理重定向。
  • HTTPCookieProcessor: 用于处理Cookies 。
  • ProxyHandler:用于设置代理,默认代理为空。
  • HπPPasswordMgr:用于管理密码,它维护了用户名和密码的表。
  • HTTPBasicAuthHandler:用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。

  另外一个比较重要的类就是OpenerDirector,我们可以称之为Opener,之前用过的urlopen()实际上也属于Opener,它是一个urllib提供给我们的封装好了的Opener。

  那么,为什么要引人Opener呢?因为需要实现更高级的功能。之前使用的Request和urlopen()相当于类库为你封装好了极其常用的请求方法,利用它们可以完成基本的请求,但是现在不一样了,我们需要实现更高级的功能,所以需要深入一层进行配置,使用更底层的实例来完成操作,所以这里就用到了Opener 。

  使用Opener需要用到open()方法,返回的类型和urlopen()如出一辙,那么,它和Handler 有什么关系呢?简而言之,就是利用Handler来构建Opener 。

验证

对于需要账户验证才能登陆查看网页来说,需要使用Handler构建opener来进行模拟验证。

from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener
from urllib.error import URLError

username = '程一夕'
password = '#importali'
url = 'https://account.aliyun.com/login/login.htm?oauth_callback=http%3A%2F%2Fdns.console.aliyun.com%2F#/dns/setting/baibainote.pro'

p = HTTPPasswordMgrWithDefaultRealm()
p.add_password(None, url, username, password)
# 实例化HTTPBasicAuthHandler对象,其参数是HTTPPasswordMgrWithDefaultRealm对象
auth_handler = HTTPBasicAuthHandler(p)
opener = build_opener(auth_handler)

try:
    result = opener.open(url)
    html = result.read().decode('utf-8')
    result = opener.open(url)
    print(html)
except URLError as e:
    print(e.reason)

代理

from urllib.error import URLError
from urllib.request import  ProxyHandler, build_opener

proxy_handler = ProxyHandler({
    'http': '127.0.0.1:12308'
})

opener = build_opener(proxy_handler)
try:
    response = opener.open('https://www.baidu.com')
    print(response.read().decode('utf-8'))
except URLError as e:
    print(e.reason)

Cookies

可通过下面的方式来获取一个网站的cookie:

import http.cookiejar, urllib.request

cookie = http.cookiejar.CookieJar()
# 使用HTTPCookieProcessor来构建handler
handler = urllib.request.HTTPCookieProcessor(cookie)
# 利用生成的handler来构建opener
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
for item in cookie:
    print(item.name + "=" + item.value)

上述输出:

BAIDUID=D450CE81692242D57AF5680E33E6F545:FG=1
BIDUPSID=D450CE81692242D57AF5680E33E6F545
H_PS_PSSID=1423_21104_28328_28131_28267_22073
PSTM=1547712883
delPer=0
BDSVRTM=0
BD_HOME=0

将cookie保存为Mozila的文件形式,可以使用如下代码:

import http.cookiejar, urllib.request

# 将cookie保存为Mozila游览器的cookie格式
cookie = http.cookiejar.MozillaCookieJar('cookies.txt')
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)

这里的cookies.txt文件内容为:

# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file!  Do not edit.

.baidu.com	TRUE	/	FALSE	3695196365	BAIDUID	3941F31A3424DAB2A92D7F2AA1696281:FG=1
.baidu.com	TRUE	/	FALSE	3695196365	BIDUPSID	3941F31A3424DAB2A92D7F2AA1696281
.baidu.com	TRUE	/	FALSE		H_PS_PSSID	1454_21093_28329_28131_28267
.baidu.com	TRUE	/	FALSE	3695196365	PSTM	1547712718
.baidu.com	TRUE	/	FALSE		delPer	0
www.baidu.com	FALSE	/	FALSE		BDSVRTM	0
www.baidu.com	FALSE	/	FALSE		BD_HOME	0

当然了,也可以改为LWP格式的cookie:

cookie = http.cookiejar.LWPCookieJar('cookies.txt')

此时,cookies.txt文件的内容就变为:

#LWP-Cookies-2.0
Set-Cookie3: BAIDUID="DC98555BA602B4179B60D70967807CE0:FG=1"; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2087-02-04 11:34:32Z"; version=0
Set-Cookie3: BIDUPSID=DC98555BA602B4179B60D70967807CE0; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2087-02-04 11:34:32Z"; version=0
Set-Cookie3: H_PS_PSSID=1442_21125_18559_28329_28132_28267; path="/"; domain=".baidu.com"; path_spec; domain_dot; discard; version=0
Set-Cookie3: PSTM=1547713226; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2087-02-04 11:34:32Z"; version=0
Set-Cookie3: delPer=0; path="/"; domain=".baidu.com"; path_spec; domain_dot; discard; version=0
Set-Cookie3: BDSVRTM=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
Set-Cookie3: BD_HOME=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0

得到了LWP格式的cookie后,我们就可以从cookies.txt文件中读内容:

import http.cookiejar, urllib.request

cookie = http.cookiejar.LWPCookieJar()
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'))

上述代码运行正常的话,则会输出百度首页的源码。

使用error模块处理异常

1. URLError

  URLError 类来自urllib库的error 模块,它继承自OSError类,是error 异常模块的基类,由request模块产生的异常都可以通过捕获这个类来处理。

from urllib import request, error

try:
    response = request.urlopen('https://www.baidu.com')
except error.URLError as e:
    print(e.reason)

2. HTTPError

  它是URLError的子类,专门用来处理HTTP请求错误,比如认证请求失败等。它有如下3 个属性:

  • code: 返回HTTP 状态码,比如404 表示网页不存在, 500 表示服务器内部错误等。
  • reason: 同父类一样,用于返回错误的原因。
  • headers: 返回请求头
from urllib import request, error

try:
    response = request.urlopen('https://cuiqingcai.com/index.html')
except error.HTTPError as e:
    print(e.reason, e.headers)

# /* ------------------- 输出: --------------------*\
# Not Found 
# # Server: nginx/1.10.3 (Ubuntu)
# # Date: Thu, 17 Jan 2019 11:44:26 GMT
# # Content-Type: text/html; charset=UTF-8
# # Transfer-Encoding: chunked
# # Connection: close
# # Vary: Cookie
# # Expires: Wed, 11 Jan 1984 05:00:00 GMT
# # Cache-Control: no-cache, must-revalidate, max-age=0
# # Link: ; rel="https://api.w.org/"

由于HTTPError是URLError的子类,于是可以先捕获子类的异常再捕获父类的异常:

from urllib import request, error

try:
    response = request.urlopen('https://cuiqingcai.com/index.html')
except error.HTTPError as e:
    print(e.reason, e.code, e.headers)
except error.URLError as e:
    print(e.reason)
else:
    print('Request.successfully')

使用parse模块解析链接

  urllib 库里还提供了parse模块,它定义了URL处理的标准接口,例如实现URL 各部分的抽取、合并以及链接转换。它支持如下协议的URL 处理:file 、ftp 、gopher等。

1. urlparse()

urlparse()定义:

urlparse(urlstring, scheme, allow_fragments)
# urlstring: 待解析的URL字符串
# scheme: 默认协议,如果urlstring中没有包含协议信息,则urlparse的结果中scheme字段的值记为该默认值
# allow_fragments: 是否忽略fragment。如果设为False,则fragment部分会被忽略,它会被解析为path,params或者query的一部分
from urllib.parse import urlparse

result = urlparse('http://www.baidu.com/index.html;user?id=5#coment')
print(type(result), result)

# /* ------------------- 输出: --------------------*\
# 
#    ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='coment')

标准URL格式:scheme://netloc/path;params?query#fragment

  • scheme:协议(?/前面)
  • netloc:域名(第一个/符号前面)
  • path:访问路径(域名后面)
  • params:参数
  • query:查询条件(?问号后面,一般用作GET类型的URL)
  • fragment:锚点(#号后面)

2. urlunparse()

  有了urlparse(),相应地就有了它的对立方法urlunparse()。它接受的参数是一个可迭代对象,但是它的长度必须是6,否则会抛出参数数量不足或者过多的异常。

from urllib.parse import urlunparse

data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
# urlunparse()的参数可以是列表,元组或其他类型
print(urlunparse(data))

# /* ------------------- 输出: --------------------*\
# http://www.baidu.com/index.html;user?a=6#comment

3. urlsplit()

from urllib.parse import urlsplit

result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment')
print(result)
# SplitResult()是一个元组类型,既可以用属性获取值,也可以使用数字下标
print(result.scheme)
print(result[0])

# /* ------------------- 输出: --------------------*\
# SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')
# http
# http

4. urlunsplit()

  与urlunparse()类似,它也是将链接各个部分组合成完整链接的方法,传人的参数也是一个可迭代对象,例如列表、元组等,唯一的区别是长度必须为5 。

from 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()

  有了urlunparse()和urlunsplit()方法,我们可以完成链接的合井,不过前提是必须要有特定长度的对象,且链接的每一部分都要清晰分开。

  此外,我们还有另一个便捷的方法,那就是urljoin()方法。我们可以提供一个base_url 作为第一个参数,将新的链接作为第二个参数,该方法会分析base_url中的scheme 、netloc 和path这3个内容并对新链接缺失的部分进行补充,最后返回结果。

from urllib.parse import urljoin

print(urljoin('http://www.baidu.com', 'FAQ.html'))
print(urljoin('http://www.baidu.com ', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about. html', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqingcai.com/index.php'))
print(urljoin ('http://www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com#comment', '?category=2'))

# /* ------------------- 输出: --------------------*\
# http://www.baidu.com/FAQ.html
# https://cuiqingcai.com/FAQ.html
# https://cuiqingcai.com/FAQ.html
# https://cuiqingcai.com/FAQ.html?question=2
# https://cuiqingcai.com/index.php
# http://www.baidu.com?category=2#comment
# www.baidu.com?category=2#comment
# www.baidu.com?category=2

  可以发现,base_url提供了三项内容scheme,netloc和path。如果这3项在新的链接里不存在,就予以补充;如果新的链接存在,就使用新的链接的部分。而base_url中的params、query和fragment就不起作用了。

6. urlencode()

from urllib.parse import urlencode

params = {
    'name': 'germey',
    'age': 22
}
base_url = 'http://www.baidu.com'
# urlencode()可将字典类型转化为GET方法的请求参数
url = base_url + urlencode(params)
print(url)

# /* ------------------- 输出: --------------------*\
# http://www.baidu.comname=germey&age=22

7. parse_qs()

  有了序列化,必然就有反序列化。如果我们有一串GET请求参数,利用parse_qs()方法,就可以将它转回字典。

from urllib.parse import parse_qs

query = 'name=germey&age=22'
print(parse_qs(query))

# /* ------------------- 输出: --------------------*\
# {'name': ['germey'], 'age': ['22']}

8. quote()

  该方法可以将内容转化为URL编码的格式。URL中带有中文参数时,有时可能会导致乱码的问题,此时用这个方法可以将中文字符转化为URL编码。

from urllib.parse import quote

keyword = '壁纸'
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)

# /* ------------------- 输出: --------------------*\
# https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8

9. unquote()

有了quote()方法,反之当然就有unquote()方法,它可以进行URL解码:

from urllib.parse import unquote

url = 'https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8'
print(unquote(url))

# /* ------------------- 输出: --------------------*\
# https://www.baidu.com/s?wd=壁纸

使用requests库

  虽然urllib库似乎无所不能,但其中确实有不方便的地方,比如处理网页验证和Cookies时,需要写Opener和Handler来处理,很显然,这两者的构建有点麻烦。为了更加方便地实现这些操作,就需要用到更为强大的库requests,有了它Cookies、登录验证和代理设置等操作都变得额外简单。

基本用法

import requests

# requests库的get方法与urllib库的urlopen()类似
req = requests.get('https://www.baidu.com')
print(type(req))    # Response对象
print(req.status_code)
print(type(req.text))
# print(req.text)   # 目标网址的网页源码【响应体】
print(req.cookies)

# /* ------------------- 输出: --------------------*\
# 
# 200
# 
# ]>

  requests库的get()方法可以完成一个GET请求,但更令人激动的是,其它请求类型在requests中依然只需要一句话来完成:

r = requests.post('http:/httpbin.org/post')
r = requests.put('http://httpbin.org/put')
r = requests.delete('http://httpbin.org/delete')
r = requests.head('http://httpbin.org/get')
r = requests.options('http://httpbin.org/get')

GET请求

要想在GET请求中附加其它参数,也可以用字典类型来构建:

import requests

data = {
    'name': 'germey',
    'age': 22
}
r = requests.get('http://httpbin.org/get', params=data)
print(r.text)

# /* ------------------- 输出: --------------------*\
# {
#   "args": {
#     "age": "22",
#     "name": "germey"
#   },
#   "headers": {
#     "Accept": "*/*",
#     "Accept-Encoding": "gzip, deflate",
#     "Connection": "close",
#     "Host": "httpbin.org",
#     "User-Agent": "python-requests/2.21.0"
#   },
#   "origin": "171.42.142.25",
#   "url": "http://httpbin.org/get?name=germey&age=22"
# }

抓取网页

我们试着抓取一个真正的网页,注意要改游览器标识,否则知乎会禁止抓取。

import requests
import re

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
r = requests.get('https://www.zhihu.com/explore', headers=headers)
# 使用正则表达式匹配内容
pattern = re.compile('explore-feed.*?question_link.*?>(.*?)', re.S)
titles = re.findall(pattern, r.text)
print(titles)

抓取二进制数据 | 图片,视频

  上面我们爬取的都是文本格式的数据(网页源码),但实际网页上其实有很多图片,视频,音乐等,要想抓取它们,就必须拿到它们的二进制数据。

import requests

r = requests.get('https://github.com/favicon.ico')
# print(r.text)     # 以文本格式表示的二进制数据(乱码)
# print(r.content)  # 图片的二进制bytes数据
# 向文件favicon.ico中写入r.content,即保存二进制数据为图片
with open('favicon.ico', 'wb') as f:
    f.write(r.content)

响应

  发送请求后,得到的自然就是响应。在上面的实例中,我们使用text和content 获取了响应的内容。此外,还有很多属性和方法可以用来获取响应中的其他信息,比如状态码、响应头、Cookies 等。

import requests

r = requests.get('https://www.jianshu.com')
print(type(r.status_code), r.status_code)
print(type(r.headers), r.headers)   # 响应头
print(type(r.cookies), r.cookies)
print(type(r.url), r.url)
print(type(r.history), r.history)   # 请求历史

# /* ------------------- 输出: --------------------*\
#  403
#  {'Connection': 'keep-alive', 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html', 'X-Via': '1.1 PSzjwzdx11at80:10 (Cdn Cache Server V2.0), 1.1 PShbxgdx6gr28:12 (Cdn Cache Server V2.0)', 'Date': 'Thu, 17 Jan 2019 13:44:06 GMT', 'Content-Encoding': 'gzip', 'Server': 'Tengine', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload'}
#  
#  https://www.jianshu.com/
#  []

  状态码一般用于判断请求是否发送成功。为了更方便的使用状态码,requests库中封装了一个内置的查询对象requests.codes。

import requests

r = requests.get('https://www.jianshu.com')
# ok状态码的值是200
exit() if not r.status_code == requests.codes.ok else print('Request Successfully')

POST请求

import requests

# POST表单请求要加data
data = {'name': 'germey', 'age': 22}
r = requests.post("http://httpbin.org/post", data=data)
print(r.text)

# /* ------------------- 输出: --------------------*\
# {
#   "args": {},
#   "data": "",
#   "files": {},
#   "form": {
#     "age": "22",
#     "name": "germey"
#   },
#   "headers": {
#     "Accept": "*/*",
#     "Accept-Encoding": "gzip, deflate",
#     "Connection": "close",
#     "Content-Length": "18",
#     "Content-Type": "application/x-www-form-urlencoded",
#     "Host": "httpbin.org",
#     "User-Agent": "python-requests/2.21.0"
#   },
#   "json": null,
#   "origin": "171.42.142.25",
#   "url": "http://httpbin.org/post"
# }

高级用法

1. 文件上传

import requests

# 将先前得到的favicon.icon上传
files = {'file': open('favicon.ico', 'rb')}
# 这次POST请求的参数不再是文本形式的data了,而是file
r = requests.post("http://httpbin.org/post", files=files)
print(r.text)

import requests

# 将先前得到的favicon.icon上传
files = {'file': open('favicon.ico', 'rb')}
# 这次POST请求的参数不再是文本形式的data了,而是file
r = requests.post("http://httpbin.org/post", files=files)
print(r.text)

# /* ------------------- 输出: --------------------*\
# {
#   "args": {},
#   "data": "",
#   "files": {
#     "file": "data:application/octet-stream;base64,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AAAAAAAAKysrBhYWGUcWFhiVFRUYvxUVGNkWFhfzFhYX8xUVGNkVFRi/FhYYlRYWGUcrKysGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
#   },
#   "form": {},
#   "headers": {
#     "Accept": "*/*",
#     "Accept-Encoding": "gzip, deflate",
#     "Connection": "close",
#     "Content-Length": "6665",
#     "Content-Type": "multipart/form-data; boundary=bdb66546fe28cb2ae0582c9351c7c073",
#     "Host": "httpbin.org",
#     "User-Agent": "python-requests/2.21.0"
#   },
#   "json": null,
#   "origin": "171.42.142.25",
#   "url": "http://httpbin.org/post"
# }

2. cookie

  前面说的urllib库也可以处理cookie,但相对比较复杂,而有了requests,获取和设置Cookies 只需一步即可完成。

import requests

r = requests.get('https://www.baidu.com')
print(r.cookies)    # RequestsCookieJar类型,这是一个Requests库封装的类
for key, value in r.cookies.items():
    print(key + '=' + value)
    
# /* ------------------- 输出: --------------------*\
<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
BDORZ=27315

3.会话维持

  在requests中,如果直接利用get()或post()等方法的确可以做到模拟网页的请求,但是这实际上是相当于不同的会话,也就是说相当于你用了两个浏览器打开了不同的页面。

  设想这样一个场景,第一次请求利用post()方法登录了某个网站,第二次想获取成功登录后的自己的个人信息,你又用了一次get()方法去请求个人信息页面。实际上,这相当于打开了两个浏览器,这是两个完全不相关的会话,这样其实是不能获取到个人信息的。

  当然,如果在两次请求时设置一样的cookies也的确可以做到维持会话的效果,但这显得略微繁琐。我们可以引入另一个利器——Session对象。

先看看连续发生两次GET请求能否获取cookie:

import requests

requests.get('http://httpbin.org/cookies/set/number/123456789')
r = requests.get('http://httpbin.org/cookies')
print(r.text)

# /* ------------------- 输出: --------------------*\
# {
#   "cookies": {}
# }

再使用Session对象试试:

import requests

# 使用同一个Session对象发送的GET请求就维持在同一个会话中
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)

# /* ------------------- 输出: --------------------*\
# {
#   "cookies": {
#     "number": "123456789"
#   }
# }

4. SSL证书验证

  requests 还提供了证书验证的功能。当发送HTTP 请求的时候,它会检查SSL 证书,我们可以使用verify参数控制是否检查此证书。其实如果不加verify参数的话,默认是True ,会自动验证。

import requests

response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)

  如果请求一个HTTPS 站点,但是证书验证错误时,就会报错,把verify 参数设置为False可以跳过SSL证书验证,避免产生这样的错误。

5. 代理设置

  对于某些网站,在测试的时候请求几次,能正常获取内容。但是一旦开始大规模爬取,对于大规模且频繁的请求,网站可能会弹出验证码,或者跳转到登录认证页面, 更甚者可能会直接封禁客户端的IP,导致一定时间段内无法访问。这可以通过设置代理来解决。

import requests

proxies = {
    "http": "http://127.0.0.1:12308"    # ,"https": "https://127.0.0.1:12308"
}
r = requests.get('https://www.taobao.com', proxies=proxies)
print(r.text)

6. 超时验证

  在本机网络状况不好或者服务器网络响应太慢甚至无响应时,我们可能会等待特别久的时间才可能收到响应,甚至到最后收不到响应而报错。为了防止服务器不能及时响应,应该设置一个超时时间,即超过了这个时间还没有得到响应,那就报错。这需要用到timeout 参数。这个时间的计算是发送请求到服务器返回响应的时间。

import requests

# 实际上,请求分为两个阶段,即连接(connect)和读取(read),下面设置的timeout表示这两者所耗费的时间总和
# r = requests.get('https://www.taobao.com', timeout=1)
# 可以给timeout传入一个元组分别设置这两个阶段的超时限制
r = requests.get('https://www.taobao.com', timeout=(10, 5))
# timeout默认值为None,表示无限制
print(r.status_code)

7. 身份认证

  如果网站需要登录验证用户身份才能获取到相应网页,这就需要使用requests.auth模块提供的身份认证功能。

import requests
from requests.auth import HTTPBasicAuth

username = '程一夕'
password = '#importali'
url = 'https://account.aliyun.com/login/login.htm?oauth_callback=http%3A%2F%2Fdns.console.aliyun.com%2F#/dns/setting/baibainote.pro'
r = requests.get(url, auth=HTTPBasicAuth('username', 'password'))
# 参数auth可以不用直接传HTTPBasicAuth类型的值,requests.auth模块允许给auth参数传入一个元组,这个元组会默认转换为HTTPBasicAuth类型的值,即:
# r = requests.get(url, 'username', 'password')
print(r.status_code)
print(r.text)

8. Prepared Request

  前面我们使用urllib库时用到了一个request对象来将要发送的请求信息,请求头等封装在一起,这在requests库中叫做Prepared Request,即将要发送的请求信息包。

from requests import Request, Session

url = 'http://httpbin.org/post'
data = {
    'name': 'Germay'
}
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
s = Session()
# 使用Request()对象封装请求信息
req = Request('POST', url, data=data, headers=headers)
# 将封装好的包放进s的预发请求栏里
prepped = s.prepare_request(req)
r = s.send(prepped)
print(r.text)

# /* ------------------- 输出: --------------------*\
# {
#   "args": {},
#   "data": "",
#   "files": {},
#   "form": {
#     "name": "Germay"
#   },
#   "headers": {
#     "Accept": "*/*",
#     "Accept-Encoding": "gzip, deflate",
#     "Connection": "close",
#     "Content-Length": "11",
#     "Content-Type": "application/x-www-form-urlencoded",
#     "Host": "httpbin.org",
#     "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
#   },
#   "json": null,
#   "origin": "171.42.142.25",
#   "url": "http://httpbin.org/post"
# }

正则表达式

  正则表达式不是Python 独有的,它也可以用在其他编程语言中。但是Python 的re 库提供了对整个正则表达式的实现,利用这个库,可以在Python中使用正则表达式。在Python 中写正则表达式几乎都要用到这个库,

1. match()

import re

content = 'Hello 123 4567 World_This is a Regex Demo'
print(len(content))     # 字符串的长度,即所包含的字符个数
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)

提取分组中的内容

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello\s(\d+)\sWorld', content)
print(result)
print(result.group())   # 获得匹配的全部结果
print(result.group(1))  # 提取第一个括号分组中的值
print(result.span())

# /* ------------------- 输出: --------------------*\

# <_sre.SRE_Match object; span=(0, 19), match='Hello 1234567 World'>
# Hello 1234567 World
# 1234567
# (0, 19)

贪婪和非贪婪

import re

# .表示任意字符,*表示前面的字符允许出现任意次,.*组合可以匹配到任意字符
content = 'Hello 1234567 World_This is a Regex Demo'
# ^表示字符串头部,$表示字符串尾部,一头一尾
result = re.match('^He.*(\d+).*Demo$', content)
print(result)
print(result.group(1))

# /* ------------------- 输出: --------------------*\
# <_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
# 7

  上述取出分组结果时,我们可以看到,本来我们的期望是匹配1234567这几个数字的,但匹配结果却显示只有7这一个数字,这就是贪婪模式的效果(默认)。

  也就是说,(\d+)前面的.*将前面6个数字匹配完了(尽可能多的匹配),这6个数字其实并没有进入分组内。

import re

# .表示任意字符(除换行符之外),*表示前面的字符允许出现任意次,.*组合可以匹配到任意字符
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*Demo$', content)
print(result)
print(result.group(1))

# /* ------------------- 输出: --------------------*\
# <_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
# 1234567

  给.*后加上?号后,就使得.*的匹配进入非贪婪模式,当.*匹配到Hello后面的那个空格时,由于后面的(\d+)恰好可以匹配接下来的7个数字,所有.*就不继续匹配了。

当非贪婪模式的匹配在模式字符串的末尾时会匹配不到任何字符:

import re

content = 'http://weibo.com/comment/kEraCN'
result = re.match('http.*?comment/(.*?)', content)
result2 = re.match('http.*?comment/(.*)', content)
print('result', result.group(1))
print('result2', result2.group(1))

# /* ------------------- 输出: --------------------*\
# result 
# result2 kEraCN

修饰符

同样是上面用到过的字符串,给This之后加了换行符后,运行后会发现匹配失败。

import re

content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.match('He.*?(\d+).*?Demo$', content)
print(result.group(1))

这是因为, . 不能匹配换行符,这里只需加一个修饰符re.S, 即可修正这个错误:

result = re.match('He.*?(\d+).*?Demo$', content, re.S)

  这个re.S修饰符很有用,因为HTML节点内经常会有换行,加上它,就可以匹配节点与节点之间的换行了。

下面还有一些修饰符,可以在需要的情况下使用:

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别匹配
re.M 多行匹配,影响^和$
re.S 使.匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响\w,\W,\b和\B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更加易于理解

2. search()

match()方法是从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败了。

如下,由于模式字符串开头并不和目标字符串开头匹配,所以匹配直接失败

import re

content = 'Hello 1234567 World_Thisis a Regex Demo'
result = re.match('(\d+).*?Demo$', content)
print(result)

# /* ------------------- 输出: --------------------*\
#  None

search()从前往后搜索,返回第一个匹配成功的子串:

import re

content = 'Hello 1234567 World_Thisis a Regex Demo'
result = re.search('(\d+).*?Demo$', content)
print(result)
print(result.group())

# /* ------------------- 输出: --------------------*\
# <_sre.SRE_Match object; span=(6, 39), match='1234567 World_Thisis a Regex Demo'>
# 1234567 World_Thisis a Regex Demo

3. findall()

  虽然search()可以返回匹配到的第一个子串,但如果想返回多个匹配成功的子串,那么可以使用findall()方法。

import re

html = '''

经典老歌

经典老歌列表

'''
results = re.findall('(.*?)', html, re.S) print(results) print(type(results)) for result in results: print(result) print(result[0], result[1], result[2]) # /* ------------------- 输出: --------------------*\ # [('/2.mp3', '陈慧琳">记事本\n
  • \n# # ('/2.mp3', '陈慧琳">记事本
  • \n
  • \n# /2.mp3 陈慧琳">记事本
  • #
  • # import re html = '''''' # 将目标字符串中与'|'匹配的部分替换为空串'' html = re.sub('|', '', html) print(html) results = re.findall('(.*?)
  • '
    , html, re.S) for result in results: print(result.strip()) # /* ------------------- 输出: --------------------*\ #
    #

    经典老歌

    #

    经典老歌列表

    #
      #
    • 一路上有你
    • #
    • # 沧海一声笑 #
    • #
    • # 往事随风 #
    • #
    • 光辉岁月
    • #
    • 记事本
    • #
    • # 但愿人长久 #
    • #
    #
    # 一路上有你 # 沧海一声笑 # 往事随风 # 光辉岁月 # 记事本 # 但愿人长久

    5. complie()

    complie()可以将可以将正则表达式编译为正则表达式对象,以重复利用模式字符串。

    import re
    
    content1 = '2016-12-15 12:00'
    content2 = '2016-12-17 12:55'
    content3 = '2016-12-22 13:21'
    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)
    
    # /* ------------------- 输出: --------------------*\
    # 2016-12-15  2016-12-17  2016-12-22 
    

      事实上,complie()中也可以传入re.S等修饰符,这样match()和search()中使用正则表达式就不用重复添加修饰符了。这也可以看出,complie()实际上是对正则表达式本身做了一层封装。

    你可能感兴趣的:(python爬虫)