学习参考:Python3网络爬虫开发实战
3.1 urllib
官方文档链接为 : https://docs.python.org/3/library/urllib.html
3.1.1 发送请求
1. urlopen()
import urllib.request
response = urllib.request.urlopen('http://www.baidu.com')
# print(response.read().decode('utf-8'))
print(type(response)) # 查看输出的响应类型
'''
它是一个 HTTPResposne类型的对象,主要包含 read()、 readinto()、 getheader(name)、
getheaders()、 fileno()等方法,以及 msg、 version、 status、 reason、 debuglevel、 ιlosed等属性
'''
print(response.status) # 返回结果状态码
print(response.getheaders()) # 响应头信息
print(response.getheader('Server')) # 调用 getheader() 方法并传递一个参数 Server 获取了响应头中的 Server 值
print(response.getheader('Set-Cookie'))
获取下来的结果:
• data参数
data 参数是可选的 。 如果要添加该参数,并且如果它是字节流编码格式的内容,即 bytes 类型, 则需要通过 bytes()方法转化。 另外,如果传递了这个参数,则它的请求方式就不再是 GET方式,而 是 POST方式
# data参数
import urllib.parse
import urllib.request
data = bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf8') #bytes()方法,该方法的第一个参数需要是 str (字符串)类型,需要用 urllib.parse 模 块里的 urlencode()方法来将参数字典转化为字符串;第二个参数指定编码格式,这里指定为 utf8
# 用str转,405 错误
response = urllib.request.urlopen('http://httpbin.org/post',data=data)
print(response.read())
print(response.status)
还是报405错误,提示方法不被允许
• timeout参数
timeout参数用于设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间, 还没有得 到响应 , 就会抛出异常。 如果不指定该参数 ,就会使用全局默认时间 。 它支持 HTTP, HTTPS、 FTP 请求
#timeout参数
# import urllib.request
#
# response = urllib.request.urlopen('http://www.baidu.com',timeout=1) # 超过一秒没反应就跳出异常
# print(response.status)
# print(response.read())
import urllib.request
import urllib.error
import socket
try:
response = urllib.request.urlopen('http://www.baidu.com',timeout=0.01) # 超过一秒没反应就跳出异常
print(response.status)
print(response.read())
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout): # 判断一个对象是否是一个已知的类型
print('TIME OUT')
2. Request
# request
# import urllib.request
#
# request = urllib.request.Request('http://www.baidu.com')
# response = urllib.request.urlopen(request)
# print(response.read())
'''
Request参数
URL
data:data 如果要传,必须传 bytes (字节流)类型的 。 如果它是字典,可以先用 urllib.parse模块里的 urlencode()编码。
headers:是一个字典,通过 headers参数直 接构造,也可以通过调用请求实例的 add_header()方法添加.通过修改 User-Agent 来伪装浏览器,默认的 User-Agent 是Python-urllib
origin_req_host:请求方的 host名称或者 IP地址
unverifiable:
'''
import urllib.request
import urllib.parse
url = 'http://www.baidu.com'
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
'Host':'http://www.baidu.com'
}
dict = {
'name':'abcd'
}
data = bytes(urllib.parse.urlencode(dict),encoding='utf-8')
req = urllib.request.Request(url=url,headers=headers, method='POST')
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))
3. 高级用法
Handler 各种处理器,有 专门处理登录验证的,有处理 Cookies 的,有处理代理设置的 。
https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler
urllib .request 模块里的 BaseHandler类,它是所有其他 Handler 的父类,它提 供了最基本的方法,例如 default_open()、 protocol_request()等
·验证
需要登陆验证的网址,借助HTTPBasicAuthHandler
# 验证
from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler,build_opener
from urllib.error import URLError
username = 'username'
password = 'password'
url = 'http://localhost:5000'
p = HTTPPasswordMgrWithDefaultRealm() # 实例化
p.add_password(None,url,username,password) # 调用类里面的方法
auth_handler = HTTPBasicAuthHandler(p)
opener = build_opener(auth_handler)
try:
result = opener.open(url)
html = result.read().decode('utf-8')
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':'http://',
# 'https':'https://'
# })
# opener = build_opener(proxy_handler)
# try:
# response = opener.open('http://www.baidu.com')
# print(response.read().decode('utf-8'))
# except URLError as e:
# print(e.reason)
• Cookies
# cookie
# import urllib.request
# import http.cookiejar
#
# 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)
# 保存cookies到文件,使用MozillaCookieJar
# import urllib.request
# import http.cookiejar
#
# filename = 'cookie.txt'
# cookie = http.cookiejar.MozillaCookieJar(filename)
# 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)
# 读取保存的cookie文件
# import urllib.request
# import http.cookiejar
#
# cookie = http.cookiejar.MozillaCookieJar()
# cookie.load('cookie.txt',ignore_expires=True,ignore_discard=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'))
3.1.2 处理异常
1 URLError
RLError类来自 urllib库的 error模块,它继承自 OSError类,是 error异常模块的基类,由 request 模块生的异常都可以通过捕获这个类来处理
2. HTIPError
它是 URLError的子类,专门用来处理HTTP请求错误,比如认证请求失败等。它有如下 3个属性.
3.1.3 解析链接
urllib库里还提供了parse模块,它定义了处理URL的标准接口,例如实现URL各部 分的抽取、合并以及链接转换.
1. urlparse()
# urlparse
from urllib.parse import urlparse
result = urlparse("http://www.baidu.com/index.html;user?id=S#comment")
print(result)
# ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=S', fragment='comment')
返回结果是一个 ParseResult 类型的对象,它包含 6个部分,分别是 scheme、 netloc、path、 params、 query和 fragment.
://前面的就是 scheme,代表协议;第一个/符号前面便是 netloc,即域名,后面是 path,即访 问路径;分号;前面是 params,代表参数;问号?后面是查询条件 query, 一般用作 GET类型的 URL;
井号#后面是锚点,用于直接定位页面内部的下拉位置.
scheme://netloc/path ;params?query#fragment
urllib.parse.urlparse(urlstring, scheme=”, allow_fragments=True)
2. urlunparse()
3. urlsplit()
4. urlunsplit()
5. urljoin()
6. urlencode()
# urlencode()
from urllib.parse import urlencode
params = {
'name':'germy',
'age':22
}
base_url = 'http://www.baidu.com?'
url = base_url + urlencode(params)
print(url)
# http://www.baidu.com?name=germy&age=22
7. parse_qs() 反序列化,字典类型
8. parse_qsl() 反序列化,元祖类型
9. quote()
内容转化为 URL 编码的格式 。 URL 中带有中文参数时,有时可能会导致乱码的问 题,此时用这个方法可以将巾文字符转化为 URL编码
# quote
from urllib.parse import quote
keyword = '中国'
URL = 'http://www.baidu.com/s?wd=' + quote(keyword)
print(URL)
# http://www.baidu.com/s?wd=%E4%B8%AD%E5%9B%BD
10. unquote() 解码
3.1.4 分析 Robots协议
1. Robots 协议
来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。它通常是一个叫作 rob。“.txt 的文本文件,一般放在网站的根目录下
3. robotparser
解析 robots.txt, 提供了一个 类 RobotFileParser,可以根据某网站的 robots.txt文件来判断一个爬取爬虫是否有权限来爬取这个 网页
3.2 使用 requests
其他的请求类型依然可 以用一句话来完成 .
如果要附加额外的信息,利用 params这个参数
如果想直接 解析返回结果,得到一个字典格式的话,可以直接调用 json()方法
·抓取网页
import requests
import re
# r = requests.get('http://www.baidu.com')
# r = requests.post('http://www.baidu.com')
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 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")
with open('favicon.ico','wb') as f:
f.write(r.content)
3.2.2 高级用法
1. 文件上传
# import requests
#
# files = {'file': open('favicon.ico','rb')}
# r = requests.post('http://httpbin.org/post',files = files)
# print(r.text)
2. Cookies
import requests
r = requests.get('http://www.baidu.com')
print(r.cookies)
for key, value in r.cookies.items():
print(key + "=" + value)
3 会话维持
维持同一个会话 , 也就是相当于打开一个新的浏览器选项 卡而不是新开一个浏览器。 但是我又不想每次设置 cookies,可以通过设置session来满足。
# 会话保持
import requests
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://www.httpbin.org/cookies')
print(r.text)
'''
{
"cookies": {
"number": "123456789"
}
}
'''
3.3 正则表达式
# 正则表达式
# import re
#
# content = "Hello 123 4567 World_This is a Regex Demo"
# result = re.match('^Hello\s\d{3}\s\d{4}\s\w{10}',content)
# print(result)
# print(result.group())
# print(result.span())
search(),它在匹配时会扫描整个字符串,然后返回第一个成功匹配的结 果 。
findall()方法 该方法会搜索整 个字符串,然后返回匹配正则表达式的所有内容
sub() 替换文本
compile() 将正则字符串编译成正 则表达式对象,以便在后面的匹配中复用
3.4 抓取猫眼电影排行
爬取猫眼排名100的电影信息
import requests
import re
import json
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.text
return None
def parse_one_page(html):
pattern = re.compile('.*?board-index.*?>(\d+).*?data-src="(.*?)@.*?".*?name">(.*?).*?star">(.*?).*?releasetime">(.*?)'
+ '.*?integer">(.*?).*?fraction">(.*?).*? ',re.S)
items = re.findall(pattern,html)
for item in items:
yield {
'index':item[0],
'image':item[1],
'title':item[2].strip(),
'actor':item[3].strip()[3:] if len(item[3])>3 else '',
'time':item[4].strip()[5:] if len(item[4])>5 else '',
'score':item[5].strip() + item[6].strip()
}
def write_to_file(content):
with open('result1.txt','a', encoding='utf-8') as f:
print(type(json.dumps(content)))
f.write(json.dumps(content,ensure_ascii=False)+'\n')
def main(offset):
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_one_page(url)
for item in parse_one_page(html):
write_to_file(item)
if __name__ == "__main__":
for i in range(10):
main(offset=i * 10)