网络爬虫工作流程中的第一步就是爬取网页,只有爬取网页获得了网页的源代码,才能提取其中的有效信息。而爬取网页首先就需要向Web服务器发送构造的HTTP请求,从而获得包含所需数据的HTTP响应。 本章从HTTP基本原理入手,介绍网络爬虫爬取网页的基础知识,包括HTTP请求和HTTP响应、urllib库、requests库和字符编码等内容。
目录
注:小节没分好,内容很多,慢慢看,一定要自己上手练,不懂可以发私信问。
1 HTTP基本原理
在浏览器中输入一个URL,按回车键后便会在浏览器中显示网页内容。
实际上,这个过程是浏览器向Web服务器发送了一个HTTP请求;
Web服务器接收到这个请求后进行解析和处理,然后返回给浏览器对应的HTTP响应;
浏览器再对HTTP响应进行解析,从而将网页呈现了出来。
使用Google Chrome浏览器打开百度的搜索页面为例来展示HTTP请求和响应的过程。(edge也行)
打开Google Chrome浏览器,右击页面空白处,在弹出的快捷菜单中选择“检查”选项,打开浏览器的开发者工具窗口,然后选择“Network”选项。访问https://www.baidu.com/,即可在开发者工具窗口中显示请求记录,其中每一条记录都代表一次发送请求和接收响应的过程。
在记录列表中,选择“www.baidu.com”选项,然后在其右侧打开的界面中选择“Headers”选项,即可显示HTTP请求和响应的详细信息,
请求的网址(Request URL)
请求方法(Request Method)
常见的请求方法有两种:GET方法和POST方法。 (1)GET方法,请求指定的网页信息,并返回网页内容,提交的数据最多只有1024字节。 (2)POST方法,向指定资源提交数据并进行请求处理(如提交表单或上传文件)。数据都包含在请求体中,提交的数据没有字节限制。
请求头(Request Headers)
请求头(Request Headers)
“Request Method”部分即为请求方法(GET方法)。可以看出,平时打开网站一般使用的是GET方法,也就是请求页面; 如果是向网站提交数据(如登录网站),就用到了POST方法。还有一些其他的请求方法,如HEAD、PUT、DELETE、CONNECT、OPTIONS和TRACE等,在实际编写爬虫程序时很少用到
2 urllib库
urllib库是Python内置的标准库,不需要额外安装即可使用,它包含如下四个模块
request模拟发送HTTP请求。
request模块提供了基本的构造HTTP请求的方法,同时它还可以处理授权验证(authentication)、重定向(redirection)、Cookie会话及其他内容。
urlopen()函数
urlopen()函数可以构造基本的HTTP请求,其函数原型如下:
urlopen(url,data=None,[timeout,]*,cafile=None,capath=None, cadefault=False,context=None)
- url 请求的URL。可以是一个表示URL的字符串,也可以是一个Request类型的对象。这是必传参数,其他都是可选参数。
- data 请求体信息(如在线翻译,在线答题等提交的内容)。data默认值是None,表示以GET方式发送请求;当用户给出data参数时,表示以POST方式发送请求。
- timeout 设置网站的访问超时时间,单位为秒。如果请求超出了设置的时间而没有得到响应,就会抛出异常。如果不指定该参数,就会使用全局默认时间
- cafile,capath,cadefault 用于实现可信任CA证书的HTTP请求,基本很少使用。
- context 实现SSL加密传输,基本很少使用。
调用函数后,返回一个HTTPResponse类型的对象
方法和属性 | 说 明 |
getcode()/status | 获取响应状态码 |
get_url() | 获取请求的URL |
getheaders() | 获取响应头信息,返回二元组列表 |
getheader(name) | 获取特定响应头信息 |
info() | 获取响应头信息,返回字符串 |
read()/readline() | 读取响应体 |
参考代码‘有道翻译’
import urllib.request #导入request模块
url = 'http://fanyi.youdao.com/translate?smartresult=dict&' \
'smartresult=rule' #定义url字符串
datavalue = {
'i': '苹果',
'from': 'AUTO',
'to': 'AUTO',
'smartresult': 'dict',
'client': 'fanyideskweb',
'salt': '15924715113269',
'sign': '5c3a992ac57ed879b7678ff05bb3ec44',
'ts': '1592471511326',
'bv': 'c74c03c52496795b65595fdc27140f0f',
'doctype': 'json',
'version': '2.1',
'keyfrom': 'fanyi.web',
'action': 'FY_BY_REALTlME'
} #定义datavalue参数
#编码datavalue参数
datavalue = urllib.parse.urlencode(datavalue).encode('utf-8')
#构造HTTP请求,并将返回的结果赋值给response
response = urllib.request.urlopen(url, data=datavalue)
resp = response.read().decode('utf-8') #读取网页内容并解码
print(resp) #输出网页内容
Request()函数
当HTTP请求信息较复杂时,可用Request()函数进行设置,其函数原型如下:
Request(url,data=None,headers={},origin_req_host=None, unverifiable=False, method=None)
- url:请求的URL。
- data:请求体信息,其使用方法与urlopen()函数中的data参数相同。
- headers:请求头信息,如User_Agent、Cookie和Host等,是字典类型。
- origin_req_host:客户端的host名称或者IP地址。
- unverifiable:表示这个请求是无法验证的,在默认情况下设置为False。
- method:请求方法,如GET、POST等,是字符串类型。 调用函数后,返回一个Request类型的对象,然后再通过urlopen()函数构造完整的HTTP请求。 当服务器有反爬虫机制时,可通过设置headers参数伪装成浏览器去访问网站。
参考代码‘有道翻译’
import urllib.request #导入request模块
url = 'http://fanyi.youdao.com/' #定义url字符串
#设置headersvalue参数,伪装成浏览器
headersvalue = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; '
'Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/'
'83.0.4103.97 Safari/537.36'}
#创建Request对象,并将返回的结果赋值给request
request = urllib.request.Request(url, headers=headersvalue)
#构造HTTP请求,并将返回的结果赋值给response
response = urllib.request.urlopen(request)
resp = response.read().decode('utf-8') #读取网页内容并解码
print(resp) #输出网页内容
error处理HTTP请求错误时的异常。
error模块提供了request模块产生的异常处理方法,它主要包含了URLError和HTTPError两个类。
URLError类
是error异常模块的基类,可以捕获request模块产生的异常,它具有一个reason属性(返回异常的原因)。
HTTPError类
是URLError类的子类,专门处理HTTP请求的异常,它具有3个属性,分别为reason(返回异常原因)、code(返回HTTP状态码)和headers(返回请求头)。
因为HTTPError是URLError的子类,并不能处理父类支持的异常处理,所以一般对两种异常分开捕获,可先捕获子类的异常,再捕获父类的异常。
参考代码
import urllib.request #导入request模块
import urllib.error #导入error模块
try: #处理异常
#构造HTTP请求,并将返回的结果赋值给response
response=urllib.request.urlopen('http://fanyi.youdao.com/api.htm')
except urllib.error.HTTPError as e: #捕获HTTP请求的异常
#输出HTTP请求的异常原因、状态码和请求头
print('异常原因:', e.reason)
print('状态码:', e.code)
print('请求头:\n', e.headers)
except urllib.error.URLError as e: #捕获URL异常
print(e.reason) #输出URL异常原因
else:
#如果没有异常则输出“Request Successfully”
print('Request Successfully')
parse解析、拆分和合并URL。
parse模块提供了解析URL的方法,包括URL的拆分、合并和转换。
参考代码
import urllib.parse #导入parse模块
#定义url字符串
url = 'https://f.youdao.com/?path=file&keyfrom=Nav-doc'
print(urllib.parse.urlparse(url)) #拆分url并输出结果
#合并url并输出结果
print(urllib.parse.urljoin(
'https://f.youdao.com/','?path=file&keyfrom=Nav-doc'))
#定义params字符串
params = {'path':'file', 'keyfrom':'Nav-doc'}
#编码params参数,合并url并输出结果
print('https://f.youdao.com/?'+urllib.parse.urlencode(params))
query = 'path=file&keyfrom=Nav-doc' #定义query字符串
print(urllib.parse.parse_qs(query)) #将query字符串转换成字典
print(urllib.parse.parse_qsl(query))
#将query字符串转换成列表
keyword = '网络爬虫' #定义keyword中文字符串
#将中文字符转换为URL编码,合并后赋值给url
url = 'https://f.youdao.com/?wd='+urllib.parse.quote(keyword)
print(url) #输出URL编码的url
#将URL编码转换为中文字符并输出结果
print(urllib.parse.unquote(url))
robotparser解析网站的robots.txt文件。
robotparser模块提供了分析网站Robots协议的RobotFileParser类,它可以通过分析网站的robots.txt文件来判断某网页是否能被爬取。RobotFileParser类提供了多种方法,常用的方法如下。
set_url():设置robots.txt文件的URL。
read():读取robots.txt文件并进行分析。
can_fetch():第一个参数为User_Agent,第二个参数为要爬取网页的URL,判断该网页是否能被爬取。
parse():解析robots.txt文件中某些行的内容。
mtime():返回上次抓取和分析robots.txt文件的时间。
modified():将当前时间设置为上次抓取和分析robots.txt文件的时间。
参考代码
import urllib.robotparser #导入robotparser模块
#创建RobotFileParser类对象rp
rp = urllib.robotparser.RobotFileParser()
#设置robots.txt文件的URL
rp.set_url('http://fanyi.youdao.com/robots.txt')
rp.read() #读取robots.txt文件并进行分析
user_agent = 'Baiduspider' #定义爬虫名称
#定义有道网页翻译的url
url = 'http://fanyi.youdao.com/web2/index.html'
#判断是否可以爬取有道网页翻译并输出判断结果
print(rp.can_fetch(user_agent, url))
#定义有道人工翻译网页的url
url = 'https://f.youdao.com/?path=fast&keyfrom=Nav-fast'
#判断是否可以爬取有道人工翻译网页并输出判断结果
print(rp.can_fetch(user_agent, url))
3 requests库
requests库可以实现很多功能,包括URL获取、HTTP长连接和连接缓存、HTTP会话、浏览器式的SSL验证、身份认证、Cookie会话、文件分块上传、流下载、HTTP(S)代理功能、连接超时处理等。
import requests #导入requests模块
#发送HTTP请求,并将返回结果赋值给r
r = requests.get('https://www.douban.com/')
print('响应类型:', type(r)) #输出响应类型
print('请求的URL:', r.url) #输出请求的URL
print('响应状态码:', r.status_code) #输出响应状态码
print('请求头:', r.request.headers) #输出请求头
requests库不是Python内置的标准库,使用之前需要安装。
PyCharm中安装requests库_技术人小柒的博客-CSDN博客_pycharm安装requests库
请求方法
GET方法
GET方法通过get()函数实现,其函数原型如下:
get(url,params=None,**kwargs)
(1)url:请求的URL。这是必传参数,其他都是可选参数。
(2)params:字典或字节序列,作为参数增加到url中。
(3)**kwargs:控制访问的参数,如headers、cookies、timeout和proxies等。
调用函数后,返回一个Response类型的对象
POST方法
POST方法通过post()函数实现,其函数原型如下:
post(url,data=None,json=None,**kwargs)
(1)url:请求的URL。这是必传参数,其他都是可选参数。
(2)data:字典、字节序列或文件对象,作为请求体的内容。
(3)json:JSON格式的数据,作为请求体的内容。
(4)**kwargs:控制访问的参数,如params、headers、cookies、timeout和proxies等。
post()函数同样返回一个Response类型的对象。
参考代码
import requests #导入requests模块
datavalue = {'name': '11111',
'password': '222222',
'remember': 'false'
} #定义datavalue参数
#定义url字符串
url = 'https://accounts.douban.com/j/mobile/login/basic'
#发送HTTP请求,并将返回结果赋值给r
r = requests.post(url, data=datavalue)
print(type(r)) #输出返回对象类型
print(r.url) #输出请求的URL
print(r.status_code) #输出返回状态码
print(r.request.headers) #输出请求头
传递URL参数
有时网站会通过URL来传递查询参数,这时可使用get()函数或post()函数的params参数进行设置
参考代码
import requests #导入requests模块
paramsvalue = {'q': 'Python'} #定义字典形式的paramsvalue参数值
#将paramsvalue作为参数增加到url中并发送请求,将返回结果赋值给r
r=requests.get('https://www.douban.com/search',params=paramsvalue)
print(r.url) #输出url
定制请求头
在requests库中,get()或post()函数可以直接传递字典形式的User_Agent信息给headers参数实现定制请求头
参考代码
import requests #导入requests模块
url = 'https://www.douban.com/' #定义url字符串
headersvalue = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
} #设置请求头的User-Agent信息
#不设置headers,发送HTTP请求,并将返回结果赋值给r
r = requests.get(url)
print(r.status_code) #输出返回状态
print(r.request.headers) #输出请求头
#设置headers,发送HTTP请求,并将返回结果赋值给r
r = requests.get(url, headers=headersvalue)
print(r.status_code) #输出返回状态
print(r.request.headers) #输出请求头
获取二进制文件
图片、音频、视频等文件本质上是由二进制码组成的,有特定的保存格式和对应的解码方式,想要爬取这些文件,需要获取它们的二进制数据。
参考代码
import requests #导入requests模块
r=requests.get('https://img9.doubanio.com/view/subject/s/public/s33643895.jpg') #发送HTTP请求,并将返回结果赋值给r
#print(r.content) #输出二进制类型返回内容
#print(r.text) #输出文本类型返回内容
#保存二进制类型返回内容为jpg文件
with open('fengmian.jpg', 'wb') as f:
f.write(r.content)
4 字符编码
在Python 3中,字符串使用str或bytes类型。str类型默认为Unicode编码格式,该编码格式不适合用来传输和存储,所以需要将str类型进行编码,转换成bytes类型(如UTF-8、GBK等格式)。
在做编码转换时,通常需要以Unicode作为中间编码,即先将其他编码的字符串解码(decode()方法)成Unicode,再从Unicode编码(encode()方法)成另一种编码。
如果要查看具体的编码类型,可以使用chardet模块中的detect()方法。
使用encode()方法和decode()方法实现Unicode和UTF-8之间的编码和解码,输出数据类型、编码和解码结果。
import chardet #导入chardet模块
str2byte = '网络爬虫'.encode('utf-8') #UTF-8编码
print(str2byte) #输出编码字符串
print(type(str2byte)) #输出编码字符串类型
print(chardet.detect(str2byte)) #检测编码类型
byte2str = str2byte.decode('utf-8') #解码
print(byte2str) #输出解码字符串
print(type(byte2str)) #输出解码字符串类型
在使用Python编写爬虫程序时,大多数网页的编码方式都是UTF-8,requests会自动对来自服务器的内容进行解码。但是有一些网页采用的是GBK等其他编码方式,或者使用gzip进行了压缩,返回的内容中包含的中文会出现乱码的情况。 因此,在解析网页之前,需要检查请求返回内容的编码方式,并进行解码,获取正确的网页内容。