爬虫基础
1 爬虫概念
1.1 爬虫的概念
网络爬虫也叫网络蜘蛛,它特指一类自动批量下载网络资源的程序,这是一个比较口语化的定义。
更加专业和全面对的定义是:网络爬虫是伪装成客户端与服务端进行数据交互的程序。
1.2 爬虫的应用
- 数据采集
大数据时代来临,数据就是核心,数据就是生产力,越来越多的企业开始注重收集用户数据,而爬虫技术是收集数据的一种重要手段。 - 搜索引擎
百度,谷歌等搜索引擎都是基于爬虫技术。 - 模拟操作
爬虫也被广泛用于模拟用户操作,测试机器人,灌水机器人等
1.3 爬虫开发的难重点
- 数据的获取
网络公共资源都是为用户准备的,为了避免被爬虫采集,服务端会设置非常多的图灵测试,阻止爬虫的恶意爬取,也即是反爬措施。爬虫开发工程师在开发爬虫时,需要解决这些反爬措施。我们在开发爬虫的过程中,有很大一部分的工作就是处理这些反爬措施。 - 采集的速度
大数据时代,需要巨大的数据量,动辄千万条的级别,甚至上亿条。如果采集速度跟不上,耗时过长,那么就达不到商业要求。一般我们会采取并发以及分布式来解决速度上的问题。这也是爬虫开发过程中的另外一个重心
2 会话技术
2.1 Cookie
http是无状态的,那服务端怎么区分同一个用户的连续请求呢,这就用到了会话技术:cookie和session。
Cookie有时也用其复数形式 Cookies,英文是饼干的意思。指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。最新的规范是 RFC6265 。
Cookie其实就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。 服务器在接收到Cookie以后,会验证Cookie的信息,以此来辨别用户的身份。
Cookie可以理解为一个凭证
2.2 Session
Session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。这个词在各个领域都有在使用。
而我们web领域,一般使用的是其本义,一个浏览器窗口从打开到关闭这个期间。
Session的目的则是,在一个客户从打开浏览器到关闭浏览器这个期间内,发起的所有请求都可以被识别为同一个用户。而实现的方式则是,在一个客户打开浏览器开始访问网站的时候,会生成一个cookie,SessionID,这个ID每次的访问都会带上,而服务器会识别这个SessionID并且将与这个SessionID有关的数据保存在服务器上。由此来实现客户端的状态识别。因此session是基于cookie的
Session与Cookie相反,Session是存储在服务器上的数据,只由客户端传上来的SessionId来进行判定,所以相对于Cookie,Session的安全性更高。
一般SessionID会在浏览器被关闭时丢弃,或者服务器会验证Session的活跃程度,例如30分钟某一个SessionID都没有活跃,那么也会被识别为失效
3 socket
socket基本实现步骤:
- 创建客户端
- 连接服务端
- 客户端向服务端发送请求报文
- 服务端向客户端返回响应报文
- 断开链接
import socket
client = socket.socket() # 创建客户端
client.connect((domain, port_num)) # 连接服务端
request_url = 'GET /index?id=1&name=aa HTTP/1.0\r\nHost: baidu.com\r\n\r\n' # 请求报文
client.send(request_url.encode()) # 发送请求数据
client.recv(1024) # 接收1024字节的数据 返回的数据类似于放置于管道中,需要一点一点取,再取数据的这个过程就是阻塞状态
client.close() # 断开链接
4 urllib库
urllib 是一个用来处理网络请求的python标准库,它包含4个模块:
urllib.requests >> 请求模块,用于发起网络请求
urllib.parse >> 解析模块,用于解析URL
urllib.error >> 异常处理模块,用于处理request引起的异常
urllib.robotparse >> 用于解析robots.tex文件
4.1 urllib.request
request模块主要负责构造和发起网络请求,并在其中添加Headers,Proxy等。
利用它可以模拟浏览器的请求发起过程。
4.11 发起网络请求
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
# urlopen是一个简单发送网络请求的方法。它接收一个字符串格式的url,它会向传入的url发送网络请求,然后返回结果,只能进行最基本的请求,不能带headers,proxy等参数
from urllib import request
response = request.urlopen(url='www.baidu.com/get')
#urlopen默认会发送get请求,当传入data参数时,则会发起POST请求。data参数是字节类型、者类文件对象或可迭代对象。
from urllib import request
response = request.urlopen(url='www.baidu.com/post',
data=b'username=aa&password=bb'
)
# 还才可以设置超时,如果请求超过设置时间,则抛出异常。timeout没有指定则用系统默认设置,timeout只对,http,https以及ftp连接起作用。它以秒为单位,比如可以设置timeout=0.1 超时时间为0.1秒。
response = request.urlopen(url='www.baidu.com', timeout=1)
4.12 添加添加headers
添加headers需要使用Request方法来进行
from urllib import request
headers = {
'User-Agant':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
} # 谷歌浏览器的User-Agant
response = request.Request('http://www.baidu.com', headers=headers)
4.13 操作cookie
操作cookie也需要使用Request方法来进行
from urllib import request
from http import cookiejar
my_cookie = cookiejar.Cookiejar() # 创建一个cookie对象
cookies = request.HTTPCookieProcessor(my_cookie) # 创建一个cookies处理器
opener = request.build_opener(cookies) # 自定义一个cookie处理opener对象
# 后续发送都使用opener对象来发送
response = opener.open('http://www.baidu.com')
4.14 设置代理IP
操作IP也需要使用Request方法来进行
from urllib import request
import random
# 代理ip池
ip_port_list = ['171.35.166.127:9999','61.145.48.31:34673','27.43.185.36:9999']
proxy ={'http':random.choice(ip_port_list)}
proxies = request.ProxyHandler(proxy) # 代理处理器
opener = request.build_opener(proxies) # 自定义一个ip处理opener对象
# 后续发送都使用opener对象来发送
response = opener.open('http://www.baidu.com')
4.15 urllib.response
urlib库中的类或或者方法,在发送网络请求后,都会返回一个urllib.response的对象。它包含了请求回来的数据结果。它包含了一些属性和方法,供我们处理返回的结果
方法 格式 描述
read() response.read() >> 获取响应返回的数据,只能用一次
readline() response.readline() >> 读取一行响应返回的数据
info() response.info() >> 获取响应头信息
geturl() response.geturl() >> 获取访问的url
getcode() response.getcode() >> 返回状态码
4.2 urllib.parse
parse模块是一个工具模块,提供了需要对url处理的方法,用于解析url。
-
parse.quote() >> url中只能包含ascii字符,在实际操作过程中,get请求通过url传递的参数中会有大量的特殊字符,例如汉字,那么就需要进行url编码。同时可以用parse.unquote() 将parse.quote()转化后的结果转化为汉字显示 。
from urllib import parse url = 'http://www.baidu.com/?name={}' name = parse.quote('名字') quote_url = url.format(name) print('quote转换结果:{}'.format(quote_url)) # quote转换结果:http://www.baidu.com/?name=%E5%90%8D%E5%AD%97 unquote_url = parse.unquote(quote_url) print('unquote转换结果:{}'.format(unquote_url)) # unquote转换结果:http://www.baidu.com/?name=名字 -
parse.urlencode() >>在发送请求的时候,往往会需要传递很多的参数,如果用字符串方法去拼接会比较麻烦,parse.urlencode()方法就是用来拼接url参数的。同时可以用parse.parse_qs() 将parse.urlencode() 转化后结果的转化为字典
from urllib import parse params = {'wd': '测试', 'arg': '方法'} encode_params = parse.urlencode(params) print('urlencode转化结果:{}'.format(encode_params)) # urlencode转化结果:wd=%E6%B5%8B%E8%AF%95&arg=%E6%96%B9%E6%B3%95 parse_qs_params = parse.parse_qs(encode_params) print('parse_qs转化结果:{}'.format(parse_qs_params)) # parse_qs转化结果:{'wd': ['测试'], 'arg': ['方法']}
4.3 urllib.error
error模块主要负责处理异常,如果请求出现错误,我们可以用error模块进行处理
主要包含URLError和HTTPError
- URLError:是error异常模块的基类,由request模块产生的异常都可以用这个类来处理
- HTTPError:是URLError的子类,主要包含三个属性:
- Code:请求的状态码
- reason:错误的原因
- headers:响应的报头
4.4 urllib.robotparse
robotparse模块主要负责处理爬虫协议文件,robots.txt.的解析
Robots协议(也称为爬虫协议、机器人协议等)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。
robots.txt文件是一个文本文件,使用任何一个常见的文本编辑器,比如Windows系统自带的Notepad,就可以创建和编辑它 [1] 。robots.txt是一个协议,而不是一个命令。robots.txt是搜索引擎中访问网站的时候要查看的第一个文件。robots.txt文件告诉蜘蛛程序在服务器上什么文件是可以被查看的。
5 urllib3
urllib3 是一个基于python3的功能强大,友好的http客户端。越来越多的python应用开始采用urllib3.它提供了很多python标准库里没有的重要功能。
urllib3不是标准库,因此需要安装:pip install urllib3
5.1 urllib3基本使用方式(GET)
"""
源码中urllib3的request请求方法中需要传入的参数
def request(self, method, url, fields=None, headers=None, **urlopen_kw):
参数 描述
method 请求方式
url 请求的url
fields post请求的数据
headers 请求头
"""
import urllib3
my_request = urllib3.PoolManager() # 实例化一个PoolManager请求对象
response = my_request.request('GET', 'http://www.baidu.com') # 发送请求获取响应对象 返回的是一个对象()
print(response) # <urllib3.response.HTTPResponse object at 0x00000251133D1240>
data = response.data # 从响应对象中获取响应数据
5.2 urllib3 POST请求方式
import urllib3
my_request = urllib3.PoolManager() # 实例化一个PoolManager请求对象
fields = {'name':'Hello World!'}
response = my_request.request('POST', fields=fields, url='http://www.baidu.com') # 发送请求获取响应对象 返回的是一个对象
data = response.data # 从响应对象中获取响应数据
5.3 Response content
http响应对象提供status, data,和header等属性
import urllib3
my_request = urllib3.PoolManager() # 实例化一个PoolManager请求对象
response = my_request.request('GET', 'http://www.baidu.com') # 发送请求获取响应对象 返回的是一个对象()
# print(response) # <urllib3.response.HTTPResponse object at 0x00000251133D1240>
data = response.data # 从响应对象中获取响应数据
status = response.status # 获取响应状态
headers = response.headers # 获取响应头
# print(data)
print('响应状态为:{}'.format(status)) # 响应状态为:200
print('响应头为:{}'.format(headers)) # 响应头为:HTTPHeaderDict({'Accept-Ranges': 'bytes', 'Cache-Control': 'no-cache', 'Connection': 'Keep-Alive', 'Content-Length': '14615', 'Content-Type': 'text/html', 'Date': 'Sun, 13 Oct 2019 03:19:35 GMT', 'Etag': '"5d8b1fec-3917"', 'Last-Modified': 'Wed, 25 Sep 2019 08:06:04 GMT', 'P3p': 'CP=" OTI DSP COR IVA OUR IND COM "', 'Pragma': 'no-cache', 'Server': 'BWS/1.1', 'Set-Cookie': 'BAIDUID=81557A2673E9465262FFA8574077B8EE:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, BIDUPSID=81557A2673E9465262FFA8574077B8EE; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, PSTM=1570936775; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com', 'Vary': 'Accept-Encoding', 'X-Ua-Compatible': 'IE=Edge,chrome=1'}
返回JSON数据格式
import urllib3
import json
my_request = urllib3.PoolManager() # 实例化一个PoolManager请求对象
response = my_request.request('GET', 'http://httpbin.org/ip') # 发送请求获取响应对象 返回的是一个对象()
data = response.data # 从响应对象中获取响应数据
json_data = json.loads(data.decode('utf-8')) # 只适用于返回数据为字典字符串的数据
print(json_data) # {'origin': '自己的ip, 自己的ip'}
返回Binary数据格式
响应返回的数据都是字节类型,对于大量的数据我们通过stream来处理更好
在视频播放时视频处理一般是边看边下载,当看到中途时断网了,一会儿网又来了,视频还是接着刚才看的下载,这就是采用了流的方式来处理数据。一点一点下载。
import urllib3
my_request = urllib3.PoolManager() # 实例化一个PoolManager请求对象
response = my_request.request('GET', 'http://httpbin.org/bytes/1024', preload_content=False) # 发送请求获取响应对象 返回的是一个对象
# 采用流的方式来处理
for chunk in response.stream(32):
print(chunk)
# 或者将数据作为文件来处理
for line in response:
print(line)
5.4 设置代理IP
import urllib3
proxy = urllib3.ProxyManager('http://114.239.255.74:808')
res = proxy.request('GET', 'http://httpbin.org/ip')
print(res.data)
5.5 设置请求头
import urllib3
import json
http = urllib3.PoolManager()
res = http.request('GET', 'http://httpbin.org/headers',headers={'key':'value'})
print(json.loads(res.data)) # {'headers': {'Accept-Encoding': 'identity', 'Host': 'httpbin.org', 'Key': 'value'}}
5.6 Query parameters
GET,HEAD,DELETE请求,可以通过提供字典类型的参数fields来添加查询参数:
import urllib3
import json
http = urllib3.PoolManager()
res = http.request('GET', 'http://httpbin.org/get',fields={'key':'value'})
print(json.loads(res.data)['args']) # {'key': 'value'}
对于post和put请求,如果需要查询参数,需要通过url编码将参数编码成正确格式然后拼接到url中:
import urllib3
import json
from urllib.parse import urlencode
http = urllib3.PoolManager()
encoded_args = urlencode({'arg':'value'})
url = 'http://httpbin.org/post?'+encoded_args
res = http.request('POST',url)
print(json.loads(res.data.decode('utf-8'))['args']) # {'arg': 'value'}
5.7 Request data
Form data:
对于put和post请求,需要提供字典类型的参数field来传递form表单数据
import urllib3
import json
from urllib.parse import urlencode
http = urllib3.PoolManager()
res = http.request('POST', 'http://httpbin.org/post',fields={'field':'value'})
print(json.loads(res.data.decode('utf-8'))['form']) # {'field': 'value'}
JSON:
当我们需要发送json数据时,我们需要在request中传入编码后的二进制数据类型的body参数,并制定Content-Type的请求头:
import urllib3
import json
from urllib.parse import urlencode
http = urllib3.PoolManager()
data = {'attribute':'value'}
encoded_data = json.dumps(data).encode('utf-8')
res = http.request('POST', 'http://httpbin.org/post',body=encoded_data, headers={'Content-Type':'application/json'})
print(json.loads(res.data.decode('utf-8'))['json']) # {'attribute':'value'}
Files & binary data:
对于文件上传,我们可以模仿浏览器表单的方式
import urllib3
import json
from urllib.parse import urlencode
http = urllib3.PoolManager()
with open('exmple.txt') as f:
file_data = f.read()
res = http.request('POST','http://httpbin.org/post',fields={'filefield':(example.txt,file_data),})
print(json.loads(r.data.decode('utf-8'))['files'])
对于二进制的数据上传,我们用指定body的方式,并设置Content-Type的请求头:
import urllib3
import json
from urllib.parse import urlencode
http = urllib3.PoolManager()
with open('exmple.jpg','rb') as f:
binary_data = f.read()
res = http.request('POST','http://httpbin.org/post',body=binary_data,headers={'Content-Type':'image/jpeg'})
print(json.loads(r.data.decode('utf-8')))
本文介绍了Python爬虫的基础知识,包括爬虫概念、会话技术如Cookie和Session,以及socket原理。重点讲解了urllib库的request、parse、error和robotparse模块,详细阐述了发起网络请求、操作Cookie、处理URL和异常的方法。此外,还探讨了urllib3库的使用,包括GET和POST请求、设置代理和请求头、处理Response内容等。

10万+

被折叠的 条评论
为什么被折叠?



