爬虫基础
HTTP协议
HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP是基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)
通过TCP/IP通信协议来在Client和Server之间建座桥
HTTP请求和响应就在这座桥上传递.
请求和响应中包含着相关信息.
HTTP请求流程
一次http请求的基本流程是,有客户端向服务端发起一次请求(request), 而服务器在接收到以后返回给客户端一个响应(response)。所以一次完整的http请求包含请求和响应两部分。
提供http服务的服务器, 会默认监听80端口, 也就是服务器上的服务端程序监听的端口为80端口
HTTPS协议
HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版
http协议是基于tcp/ip协议的,而https是在http协议的基础之上,再加了一层SSL/TLS协议,数据在传输过程中是加密的。
HTTPS协议的默认端口是443
URL
发送http请求时,通过url对网络资源进行定位。
HTTP请求报文
HTTP请求方法
常用请求方法
根据http标准,http请求可以使用多种请求方法。
HTTP请求头
HTTP请求正文
请求正文通常是使用POST方法进行发送的数据,GET方法是没有请求正文的。
请求正文跟上面的消息报头由一个空行隔开。
HTTP响应
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
HTTP响应状态码
当客户端向服务端发起一次请求后,服务端在返回的响应头中会包含一个HTTP状态码。
HTTP的状态码是由三位数字来表示的,由第一位数字来表示状态码的类型,一般来说有五种类型:
HTTP响应报头
HTTP协议的特点
HTTP三点注意事项:
-
HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
-
HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
-
HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
Cookie
HTTP是无状态的,协议本身不涉及保存访问记录, 也就是请求和响应过程中不保存相关记录信息, 这样的特性会造成两个现状:
1. 基于HTTP实现的应用在数据交互时, 交互速度比较快
2. 由于不记录信息, 服务器无法区分访问自己的客户端.
总结: 用户的个人信息一般会放在Cookie中(这些信息可以是账号和密码, 也可以是其他东西), 用来在访问特定页面时(如访问个人中心页面)包含在请求中, 发给服务器验证.但是Cookie若被骇客盗用, 那么骇客也能去访问用户的个人中心页面, 就像上面的第三次交互.
Session
在一个客户从打开浏览器到关闭浏览器这个期间内,发起的所有请求都可以被识别为同一个用户。而实现的方式则是,在一个客户打开浏览器开始访问网站的时候,服务器会生成一个SessionID值并和在服务器中的用户信息进行绑定, 客户端获取到SessionID后以Cookie的方式保存, 这个ID每次的访问都会带上,而服务器会识别这个SessionID由此来实现客户端的状态识别。因此session是基于cookie的
Session与Cookie相反,Session是存储在服务器上的数据,对客户端传上来的SessionID来进行判定,所以相对于Cookie,Session的安全性更高。
一般SessionID会在浏览器被关闭时丢弃,或者服务器会验证Session的活跃程度,例如30分钟某一个SessionID都没有活跃,那么也会被识别为失效。
数据采集
—urllib库
url请求模块
from urllib import request
方法:
request.Request(url)
request模块主要负责构造和发起网络请求,并在其中添加Headers,Proxy等。
利用它可以模拟浏览器的请求发起过程。
request.urlopen(url,data=None,timeout)
urlopen默认会发送get请求,当传入data参数时,则会发起POST请求。data参数是字节类型、者类文件对象或可迭代对象。
request方法和response对象方法的使用
from urllib import request
#构造请求信息
req=request.Request('http://www.baidu.com')
#发送请求 返回响应信息
response=request.urlopen(req)
#常用response对象的方法
print(response.read())#获取响应数据
print(response.info())#获取响应信息
print(response.geturl())#获取访问的url
print(response.getcode())#获取http状态码
请求头添加
from urllib import request
headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6821.400 QQBrowser/10.3.3040.400"}
#构造请求信息
req=request.Request('http://www.baidu.com',headers=headers)
#发送请求 返回响应信息
response=request.urlopen(req)
#常用response对象的方法
print(response.read().decode())#获取响应数据
操作cookie
from urllib import request
from http import cookiejar
#创建一个cookie对象
cookie=cookiejar.CookieJar()
#创建一个cookie处理器
cookies=request.HTTPCookieProcessor(cookie)
#以它为参数 创建一个opener对象
opener=request.build_opener(cookies)
#发送请求 获取响应
res=opener.open('http://www.baidu.com')
#获取响应数据
print(cookies.cookiejar)
设置代理ip
from urllib import request
#设置爬取网站地址
url='http://httpbin.org/get'
#代理地址
proxy={'http':'27.208.80.66:8060'}
#代理案处理器
proxies=request.ProxyHandler(proxy)
#创建opener对象
opener=request.build_opener(proxies)
#发送请求 获取响应
response=opener.open(url)
#打印响应信息
print(response.read().decode())
url编码模块
from urllib import parse
方法:
对特殊字符进行编码parse.quote(字符串)
对特殊字符进行解码parse.unquote(字符串)
url编码表单字典信息parse.urlencode(字典)
解码表单字典信息parse.parse_qs(字典url编码的字符串)
url异常报错模块
from urllib import error
error模块主要负责处理异常,如果请求出现错误,我们可以用error模块进行处理
主要包含URLError和HTTPError
URLError的报错使用
from urllib import error
try:
pass#爬虫代码
except error.URLError as e:
print(e)
HTTPError的报错使用
from urllib import error
try:
pass#爬虫代码
except error.HTTPError as e:
print(e.code)#获取http状态码
print(e.reason)#获取错误原因
print(e.headers)#获取响应的报头
urllib爬取实例
from urllib import request
from urllib import parse
#添加请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6821.400 QQBrowser/10.3.3040.400"}
#添加表单
formdata={"i": "快乐",
"from":" AUTO",
"to":"AUTO",
"smartresult":"dict",
"client":" fanyideskweb",
"salt":"15587539525925",
"sign":"981e5ff6edbf915b9e0238b82ce10bf5",
"ts":"1558753952592",
"bv":"64bf5afb3cca58e10e1392ed7a581832",
"doctype":"json",
"version":"2.1",
"keyfrom":"fanyi.web",
"action":"FY_BY_CLICKBUTTION"}
#url编码和字节编码表单
post_formdata=parse.urlencode(formdata).encode()
url='http://fanyi.youdao.com/translate?'
#设置代理ip
proxy={'http':'27.208.80.66:8060'}
#代理处理器
proxies=request.ProxyHandler(proxy)
#创建opener对象
opener=request.build_opener(proxies)
#构造请求信息
req=request.Request(url,data=post_formdata,headers=headers)
#发送请求 获取响应
response=opener.open(req)
#打印响应信息
print(response.read().decode())
—urllib3库
import urllib3
1.实例化PoolManager对象
http=urllib3.PoolManager()
.
2.发送request请求
response=http.request(请求方式,url,[field])
GET,HEAD,DELETE请求,可以通过提供字典类型的参数fields来添加查询参数
对于POST和PUT请求,如果需要查询参数,需要通过url编码将参数编码成正确格式然后拼接到url中
3.response对象的方法
获取http状态码response.status()
获取响应数据response.data()
获取响应头信息response.headers()
添加请求头
import urllib3
import json
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6821.400 QQBrowser/10.3.3040.400"}
http=urllib3.PoolManager()
response=http.request('GET','http://httpbin.org/get',headers=headers)
#json数据转为字典数据
print(json.loads(response.data.decode()))
设置代理ip
import urllib3
import json
proxy=urllib3.ProxyManager('http://119.190.150.181:8060')
response=proxy.request('GET','http://httpbin.org/get')
print(json.loads(response.data.decode()))
提交表单
对于put和post请求,需要提供字典类型的参数field来传递form表单数据。
import urllib3
import json
proxy=urllib3.ProxyManager('http://119.190.150.181:8060')
response=proxy.request('POST','http://httpbin.org/post',fields={'name':'jack'})
print(json.loads(response.data.decode()))
发送json数据
当我们需要发送json数据时,我们需要在request中传入编码后的二进制数据类型的body参数,并制定Content-Type的请求头
import urllib3
import json
encode_data=json.dumps({'name':'jack'})#字典数据转json数据
proxy=urllib3.ProxyManager('http://119.190.150.181:8060')
response=proxy.request('POST','http://httpbin.org/post',headers={'Content-Type':'application/json'},body=encode_data)
print(json.loads(response.data.decode()))
二进制数据分割传输
—requests库
官方中文文档:https://2.python-requests.org//zh_CN/latest/
发送请求的方法
请求方法 | 说明 |
---|---|
requests.request(method,url) | 获取HTML网页的通用方法 |
requests.get(url) | 获取HTML网页的主要方法,对应HTTP的GET |
requests.post(url) | 向HTML网页提交POST请求方法,对应HTTP的POST |
requests.put(url) | 向HTML网页提交PUT请求的方法,对应HTTP的RUT |
requests.delete(url) | 向HTML页面提交删除请求,对应HTTP的DELETE |
requests.head(url) | 获取HTML网页头的信息方法,对应HTTP的HEAD |
requests.options(url) | 发送“探测”请求以确定某个目标地址的请求具有怎样的约束 |
requests.patch(url) | 向HTML网页提交局部修改请求,对应于HTTP的PATCH |
请求方法的参数
请求方法的参数 | 说明 |
---|---|
headers(字典类型) | 请求头设置 |
proxies(字典类型) | 代理设置 |
params(字典类型) | 传递URL参数 |
data(字典类型) | 提交表单数据 |
json(字典类型) | 提交json数据 |
cookies(字典类型) | cookie设置 |
timeout(数值类型) | 设置访问超时时间(单位为秒) |
allow_redirects(布尔类型) | 重定向设置(参数值True为允许,False为禁止) |
verify(布尔类型) | 证书验证设置(参数值True为打开,False为关闭) |
解除verify证书验证关闭后的警报:
import urllib3
urllib3.disable_warnings()
获取响应对象属性
响应对象的属性 | 说明 |
---|---|
r.headers | 获取响应的报头 |
r.cookies | 获取服务器返回的cookie |
r.status_code | HTTP请求的返回状态 |
r.url | 获取响应内容的url |
r.encoding | 设置响应数据的编码方式 |
r.content | HTTP响应内容的二进制形式 |
r.text | HTTP响应内容的字符串形式(url对应的html页面内容) |
r.json() | HTTP响应内容的json数据转换为字典类型 |
Session对象
#引入urllib3模块
import urllib3
#实例化session对象
s=urllib3.Session()
#发送请求
r=s.get('http://www.baidu.com')
session发送请求格式
s.请求方法(url,各种请求方法的参数)
请求方法:get/post/put/delete/options/head/patch
各种请求方法的参数:headers/proxies/params/data/json/timeout/allow_redirects/verify
session对象的属性
session对象的属性设置 | 说明 |
---|---|
s.headers(字典类型) | 请求头设置 |
s.proxies(字典类型) | 代理设置 |
s.params(字典类型) | 传递URL参数 |
s.data(字典类型) | 提交表单数据 |
s.json(字典类型) | 提交json数据 |
s.cookies(字典类型) | cookie设置 |
s.timeout(数值类型) | 设置访问超时时间(单位为秒) |
s.allow_redirects(布尔类型 | 重定向设置(参数值True为允许,False为禁止) |
s.verify(布尔类型) | 证书验证设置(参数值True为打开,False为关闭) |
数据清洗
—BeautifulSoup
官方中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
安装模块:pip install beautifulsoup4
from bs4 import BeautifulSoup
soup=BeautifulSoup(响应的文本内容)#实例化
BeautifulSoup对象属性和方法 | 说明 |
---|---|
soup.标签名 | 获取标签 |
soup.标签名.name | 获取标签名 |
soup.标签名.string | 获取标签的文字内容 |
soup.标签名.parent.name | 获取标签的父标签名 |
soup.标签名['class'] | 获取标签的类的值 |
soup.find_all("标签"/ 属性名="值”) | 根据标签名或属性值查找符合的所有标签 |
soup.find("标签"/ 属性名="值”) | 根据标签名或属性值查找所有符合标签的的第一个标签 |
1.获取所有某个标签的指定属性值
for i in soup.find_all('a'):
print(i.get("href"))#获取所有a标签的href属性值
—XPath
from lxml import etree
import requests
res=requests.get('www.baidu.com')
#字符串 转换为 html
html=etree.HTML(res.text)
#html 转换为 字符串
html_to_str=etree.tostring(html,encoding='utf-8)
#使用xpath
html.xpath('//title/text()')
选取结点
路径表达式 | 说明 |
---|---|
bookstore | 选取 bookstore 元素的所有子节点 |
/bookstore | 选取根元素 bookstore |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置 |
//@lang | 选取名为 lang 的所有属性 |
谓语
路径表达式 | 说明 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素 |
/bookstore/book[position()< 3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素 |
//title[@lang=‘eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00 |
选取未知结点
路径表达式 | 说明 |
---|---|
/bookstore/* | 选取 bookstore 元素的所有子元素 |
//* | 选取文档中的所有元素 |
//title[@*] | 选取所有带有属性的 title 元素 |
选取多个路径
路径表达式 | 说明 |
---|---|
//book/title | //book/price | 选取 book 元素的所有 title 和 price 元素 |
//title | //price | 选取文档中的所有 title 和 price 元素 |
/bookstore/book/title | //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素 |
—re正则表达式
常用的匹配表达式:https://blog.csdn.net/qq_44647926/article/details/89383250
re中的方法 | 使用方法 | 说明 |
---|---|---|
re.findall(x,y) | x:寻找的字符串 y:从y字符串中寻找 | 从整个字符串里面匹配,将所有匹配到的字符以列表形式返回 |
re.match(x,y) | x:寻找的字符串 y:从y字符串中寻找 | 从起始位置开始匹配,若没有匹配到则返回None |
re.search(x,y) | x:寻找的字符串 y:从y字符串中寻找 | 从整个字符串里面匹配,匹配第一次出现的字符串 |
re.complie(正则表达式) | c=re.compile(正则表达式),c.match(字符串),c.search(字符串),c.findall(字符串) | 将正则表达式实例化,供match,search,findall方法使用 |
re.split(x,y) | x:分割界限字符串 y:从y字符串中寻找 | 从整个字符串里面匹配,将所有匹配到的字符以列表形式返回 |
re.finditer(x,y) | x:寻找的字符串 y:从y字符串中寻找 | 从整个字符串里面匹配,将所有匹配到的字符以迭代器形式返回 |
1.match
search
方法中需要用group()
来返回已经匹配到的字符串span()
来返回已经匹配的字符串的下标值
2.匹配换行\n
在re方法中传入参数re.S
3.开启对大小写不敏感
在re方法中传入参数re.l
元字符 | 说明 |
---|---|
代表数量的元字符 | |
* | 要匹配的前一个字符出现0次或者无限次 |
+ | 要匹配的前一个字符至少出现1次 |
? | 要匹配的前一个字符出现1次或者0次 |
{n} | 匹配前一个字符出现n次 |
{n,} | 匹配前一个字符至少出现n次 |
{m,n} | 匹配前一个字符出现m到n次 |
匹配边界的元字符 | |
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配一个单词的非边界 |
A|B | 匹配左右任意一个表达式 |
() | 将括号里的内容提取出来 |
.* | 找最长的匹配 尽可能多的匹配内容 |
.*? | 找最短的匹配 |
匹配通用符 | |
\d | 匹配数字 |
\D | 匹配非数字 |
\s | 匹配空白 |
\S | 匹配非空白 |
\w | 匹配单词字符(a-z, A-Z ,0-9, _) |
\W | 匹配非单词字符 |
[abcde] | 匹配时原子表中出现的每个原子都可以匹配到 |
[^abc] | 匹配除abc之外的字符 |
[0-9] | 匹配一个数字 |
[a-z]和[A-Z] | 匹配一个字母 |
[\u4e00-\u9fa5] | 匹配一个中文 |