HTTP基本原理

请求

分为四部分:请求方法(RequestMethod)、请求的网址(RequestURL)、请求头(RequestHeaders)、请求体(RequestBody)。

方法

序号 方法 描述
1 GET 从服务器获取资源。用于请求数据而不对数据进行更改。例如,从服务器获取网页、图片等。
2 POST 向服务器发送数据以创建新资源。常用于提交表单数据或上传文件。发送的数据包含在请求体中。
3 PUT 向服务器发送数据以更新现有资源。如果资源不存在,则创建新的资源。与 POST 不同,PUT 通常是幂等的,即多次执行相同的 PUT 请求不会产生不同的结果。
4 DELETE 从服务器删除指定的资源。请求中包含要删除的资源标识符。
5 PATCH 对资源进行部分修改。与 PUT 类似,但 PATCH 只更改部分数据而不是替换整个资源。
6 HEAD 类似于 GET,但服务器只返回响应的头部,不返回实际数据。用于检查资源的元数据(例如,检查资源是否存在,查看响应的头部信息)。
7 OPTIONS 返回服务器支持的 HTTP 方法。用于检查服务器支持哪些请求方法,通常用于跨域资源共享(CORS)的预检请求。
8 TRACE 回显服务器收到的请求,主要用于诊断。客户端可以查看请求在服务器中的处理路径。
9 CONNECT 建立一个到服务器的隧道,通常用于 HTTPS 连接。客户端可以通过该隧道发送加密的数据。

请求体

POST请求的Content-Type

Content-Type POST提交数据的方式
application/x-www-form-urlencoded 表单数据
multipart/form-data 表单文件上传
application/json 序列化JSON数据
text/xml XML数据

响应

响应,即Response,由服务器返回给客户端,可以分为部分:响应状态码(Responsestatuscode)、响应头(ResponseHeaders)和响应体(ResponseBody)。

响应头

Date:用于标识响应产生的时间
Last-Modified:用于指定资源的最后修改时间
Content-Encoding:用于指定响应内容的编码
server:包含服务器的信息,例如名称、版本号等。
Content-Type:文档类型,指定返回的数据是什么类型,如text/html代表返回HTML文档,application/x-javascript代表返回JavaScnpt文件,image/jpeg代表返回图片
set-cookie:设置cookie。响应头中的set-cookre用于告诉浏览器需要将此内容放在cookie中,下次请求时将cookie携带上。
Expires:用于指定响应的过期时间,可以让代理服务器或浏览器将加载的内容更新到缓存中。当再次访问相同的内容时,就可以直接从缓存中加载,达到降低服务器负载、缩短加载时间的目的

爬虫的基本原理

javascript渲染的页面

有时候,我们在用urllib或requests抓取网页时,得到的源代码和在浏览器中实际看到的不一样。这是一个非常常见的问题。现在有越来越多的网页是采用Ajax、前端模块化工具构建的,可能整个网页都是由JavaScnpt渲染出来的,也就是说原始的HTML代码就是一个空壳。

对于这样的情况,我们可以分析源代码后台丙ax接口,也可使用selenium、splash、Pyppeteer、Playwnght这样的库来模拟JavaScnpt渲染

基本库的使用

request库

抓取网页

import requests
import re

r=requests.get('https://ssr1.scrape.center/')
pattern=re.compile('<h2.*?>(.*?)</h2>',re.S)
titles=re.findall(pattern,r.text)
print(titles)

抓取二进制数据(如图片音视频)

import requests
import re

r=requests.get('https://scrape.center/favicon.ico')
with open('favicon.ico','wb') as f:
f.write(r.content)

响应

通过statuscode属性得到状态码、通过headers属性得到响应头、通过cookies属性得到cookie、通过url属性得到URL、通过history属性得到请求历史。

文件上传(files参数)

import requests

file={'aaa':open('favicon.ico','rb')}
r=requests.post('https://www.httpbin.org/post',files=file)
print(r.text)

维护sessions

import requests

s=requests.Session()
s.get('https://www.httpbin.org/cookies/set/number/123456789')
r=s.get('https://www.httpbin.org/cookies/')
print(r.text)

利用session可以做到模拟同一个会话而不用担心的问题,它通常在模拟登录成功之后,进行下一步操作时用到。session在平常用得非常广泛,可以用于模拟在一个浏览器中打开同一站点的不同页面

SSL证书认证

忽略证书认证

import requests

response=requests.get('https://ssr2.scrape.center/',verify=False)

或者设置本地证书,这可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:

超时设置

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

import requests

r=requests.get('https://www.httpbin.org/get/',timeout=1)

实际上,请求分为两个阶段:连接(connect)和读取(read)。上面设置的timeout是用作连接和读取的timeout的总和

如果要分别指定用作连接和读取的timeout,则可以传人一个元组:

import requests

r=requests.get('https://www.httpbin.org/get/',timeout=(5,30))

身份认证

import requests

r=requests.get('https://ssr3.scrape.center/',auth=('admin',"admin"))
print(r.status_code)

代理设置

import requests

proxies={
'http': 'socks5://127.0.0.1:1080',
'https': 'socks5://username:password@127.0.0.1:1080'
}
r=requests.get('https://ssr3.scrape.center/',proxies=proxies)

正则表达式

match

一个常用的匹配方法match 向它传人要匹配的字符串以及正则表达式,就可以检测这个正则表达式是否和字符串相匹配。
方法会尝试从字符串的起始位置开始匹配正则表达式,如果匹配,就返回匹配成功的结果;如果不匹配,就返回None。实例如下:

import re

content='Hello 123 4567 World_This is a Regex Demo'
result=re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}',content)
print(result)
print(result.group())
print(result.span())

将输出结果打印出来

<re.Match object; span=(0, 25), match='Hello 123 4567 World_This'>
Hello 123 4567 World_This
(0, 25)

可以看到结果是SREMatch对象,证明匹配成功。该对象包含两个方法:group方法可以输出匹配到的内容,结果是Hello 123 4567 World_This,这恰好是正则表达式按照规则匹配的内容;span方法可以输出匹配的范围,结果是(0,25),这是匹配到的结果字符串在原字符串中的位置范围。

匹配目标

可以使用括号()将想提取的子字符串括起来。()实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式依次对应每个分组,调用group方法传人分组的索引即可获取提取结果。实例如下:

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

输出结果:

<re.Match object; span=(0, 19), match='Hello 1234567 World'>
Hello 1234567 World
1234567
(0, 19)

通过这个实例,我们把字符串中的1234567提取出来了,可以看到其中数字部分的正则表达式被()括了起来。然后调用group(1)获取了匹配结果。

通用匹配

.* 万能匹配

.可以匹配任意字符(除换行符),*代表匹配前面的字符无限次

import re

content='Hello 123 4567 World_This is a Regex Demo'
result=re.match('^Hello.*Demo$',content)
print(result)
print(result.group())
print(result.span())

输出结果

<re.Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
Hello 123 4567 World_This is a Regex Demo
(0, 41)

匹配修饰符

re.match(pattern,content,re.S)

re.S表示匹配换行符

search,它在匹配时会扫描整个字符串,然后返回第一个匹配成功的结果也就是说,正则表达式可以是字符串的一部分。在匹配时,search方法会依次以每个字符作为开头扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容;如果扫描完还没有找到符合规则的字符串,就返回None

因此,为了匹配方便,尽量使用search方法

findall

获取与正则表达式相匹配的所有字符串

sub

用于修改文本

这里往sub方法的第一个参数中传人\d+以匹配所有的数字,往第二个参数中传人把数字替换成的字符串(如果去掉该参数,可以赋值为空),第三个参数是原字符串

content=re.sub('\d+,',' ',content)

compile

将字符串编译成正则表达式对象

pattern=re.compile('\d{2}:d{2}')

httpx

支持HTTP/2.0协议

import httpx
client=httpx.Client(http2=True)
response=client.get('https://spa16.scrape.center/')
print(response.text)
  • status code:状态码
  • text:响应体的文本内容
  • content:响应体的二进制内容,当请求的目标是二进制数据(如图片)时,可以使用此属性获取
  • headers:响应头,是Headers对象,可以用像获取字典中的内容一样获取其中某个Header的值
  • json:方法,可以调用此方法将文本结果转化为JSON对象