网络爬虫基础-网络请求

http 协议和Chrome抓包工具

http协议和 https协议

  1. HTTP协议:全称是HyperText Transfer Protocol,中文名称是超文本传输协议,是一种发布和接受HTML页面的方法(用于从网络传输超文本数据到本地浏览器的传送协议)。服务器端口号是80端口。
  2. HTTPS协议:是HTTP协议的加密版本,在HTTP加入了SSL层。(数据传输更加安全),服务器端口号是443端口。

在浏览器发送一个http请求的过程

  1. 当用户在浏览器的地址栏中输入一个 URL 并按回车键之后,浏览器会向 HTTP服务器发送 HTTP请求。HTTP请求主要分为“Get”和“Post”两种方法。

  2. 当我们在浏览器输入一个URL http://www.baidu.com的时候,浏览器发送一个Request请求去获取 http://www.baidu.com的html文件,服务器把Response文件对象发送回给浏览器。

  3. 浏览器分析Response中的HTML,发现其中引用了很多他文件比如 Images文件, CSS 文件, JS 文件。浏览器会自动再次发送 Request去获取图片,CSS文件,或者 JS 文件。

  4. 当所有的文件都下载成功后,网页会根据 HTML语法结构,完整的显示出来了。

Url 详解

URL 是 Uniform Resource Locator的简写,统一资源定位符。 一个 URL 由以下几部分组成:scheme://host:port/path/?query-string=xxxstring&query -string=xxx#anchor

  • scheme:代表的是访问协议,一般为http 或者 https以及 ftp 等
  • host:主机名、域名,比如/www.baidu.com。
  • port:端口号。当访问一个网站的时候,浏览器默认使用80端口
  • path:查找路径。比如:www.jianshu.com/trending/now,后面的 trending/now 就是 path。
  • query-string:查询字符串,比如: www.baidu.com/s?wd=python,后面的 wd=python就是查询字符串
  • anchor:锚点,后台一般不用管,前端用来做页面定位的。 (就相当于一个页面中有目录,目录的跳转就是锚点的作用)

在浏览器中请求一个 url,浏览器会对这个 url 进行一个编码。除英文字母,数字和部分符号外,其他的全部使用百分号+十六进制码值进行编码。

常用的请求方法

在 Http 协议中,定义了八种请求方法。这里介绍两种常用的请求方法,分别是 get 请求和post 请求。

  1. get 请求:一般情况下,只从服务器获取数据下来,并不会对服务器资源产生任何影响的时候会使用 get 请求。 get请求的参数包含在URL中,如wd=python
  2. post 请求:向服务器发送数据(登录)、上传文件等,会对服务器资源产生影响的时候会使用 post 请求 。post请求的URL不会包含数据。数据以表单的形式提交传输,会包含在请求体中。

以上是在网站开发中常用的两种方法。并且一般情况下都会遵循使用的原则。但是有的网站和服务器为了做反爬虫机制,也经常会不按常理出牌,有可能一个应该使用 get 方法的请求就一定要改成 post 请求,这个要视情况而定。

请求头常见参数

在 http 协议中,向服务器发送一个请求,数据分为三部分,第一个是把数据放在 url 中,第二个是把数据放在 body 中(在 post 请求中),第三个就是把数据放在 head 中。

这里介绍在网络爬虫中经常会用到的一些请求头参数:

  1. User-Agent:浏览器名称。这个在网络爬虫中经常会被使用到。请求一个网页的时候,服务器通过这个参数就可以知道这个请求是由哪种浏览器发送的。如果我们是通过爬虫发送请求,那么我们的 User-Agent 就是 Python,这对于那些有反爬虫机制的网站来说,可以轻易的判断你这个请求是爬虫。因此我们要经常设置这个值为一些浏览器的值,来伪装我们的爬虫。
  2. Referer:表明当前这个请求是从哪个 url 过来的。这个一般也可以用来做反爬虫技术。如果不是从指定页面过来的,那么就不做相关的响应。
  3. Cookie: http 协议是无状态的。也就是同一个人发送了两次请求,服务器没有能力知道这两个请求是否来自同一个人。因此这时候就用 cookie 来做标识。一般如果想要做登录后才能访问的网站,那么就需要发送 cookie 信息了。

常见响应状态码

  1. 200:请求正常,服务器正常的返回数据
  2. 301:永久重定向。比如在访问 www.jingdong.com 的时候会重定向到 www.jd.com。
  3. 302:临时重定向。比如在访问一个需要登录的页面的时候,而此时没有登录,那么就会重定向到登录页面。
  4. 400:请求的 url 在服务器上找不到。换句话说就是请求 url 错误。
  5. 403:服务器拒绝访问,权限不够。
  6. 500:服务器内部错误。可能是服务器出现 bug 了。

Chrome 抓包工具:

Chrome 浏览器是一个非常亲近开发者的浏览器。可以方便的查看网络请求以及发送的参数。对着网页右键->检查。然后就可以打开开发者选项。

urllib 库

urllib 库是Python 中一个最基本的网络请求库。可以模拟浏览器的行为,向指定的服务器发送一个请求,并可以保存服务器返回的数据。

urllib.request模块

在Python3 的urllib 库中,所有和网络请求相关的方法,都被集到urllib.request 模块下面了,以先来看下urlopen 函数基本的使用

request.urlopen()函数

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None,cadefault=False, context=None)

# 抓取百度源码
resp = request.urlopen('http://www.baidu.com')
# 查看返回类型
print(type(resp))
# 读取数据
print(resp.read().decode('utf-8'))

实际上,使用浏览器访问百度,右键查看源代码。你会发现,跟我们刚才打印出来的数据是一模一样的。也就是说,上面的三行代码就已经帮我们把百度的首页的全部代码爬下来了。一个基本的url 请求对应的python 代码真的非常简单。

以下对urlopen 函数的进行详细讲解:
url:请求的url。
data:请求的data,如果设置了这个值,那么将变成post 请求。

# 以post方式传输数据
data = bytes(parse.urlencode({'word': 'hello'}), encoding='utf-8')
resp = request.urlopen('https://httpbin.org/post', data=data)
print(resp.read())

返回值:返回值是一个http.client.HTTPResponse 对象,这个对象是一个类文件句柄对象。有read(size)、readline、readlines 以及getcode 等方法。

  1. read(size):返回指定字节的数目,默认为全部
  2. readline():返回一行
  3. readlines():用列表返回全部行
  4. getcode():返回状态码, resp.getcode()等价于 resp.status
  5. getheaders():返回响应头

request.urlretrieve 函数:

这个函数可以方便的将网页上的一个文件保存到本地

# 将百度的首页下载到本地
request.urlretrieve('http://www.baidu.com', 'baidu.html')

request.Request 类

如果想要在请求的时候增加一些请求头,那么就必须使用 request.Request 类来实现。比如要增加一个 User-Agent,示例代码如下:

# 增加请求头 
url = 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=' 
resp = request.urlopen(url) 
# print(resp.read().decode('utf-8'))  # 结果无意义
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',             
           'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput='            
          }
# Referer 设定是从哪个网页跳转过来的
req = request.Request(url, headers=headers) 
resp = request.urlopen(req) 
print(resp.read().decode('utf-8'))

另外,如果要使用 Post 请求,也可以使用 Request 类,这里要注意,传入的 data 一定要是 bytes 类型

#  拉勾网中,真实的数据保存在 'https://www.lagou.com/jobs/positionAjax.json?px=new&city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false' 
#  通过poisionAjax.json可以得到这个url 
#  方法为post
url = 'https://www.lagou.com/jobs/positionAjax.json?px=new&city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false'
data = {     
    'first': 'true',    
    'pn': 1,    
    'kd': 'python' } 
# bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url, headers=headers,                    
                      data=parse.urlencode(data).encode('utf-8'),                   
                      method='POST') 
resp = request.urlopen(req, timeout=5) 
print(resp.read().decode('utf-8'))

urllib.parse模块

parse模块中的内容与解析相关

parse.urlencode 函数:

用浏览器发送请求的时候,如果 url 中包含了中文或者其他特殊字符,那么浏览器会自动的给我们进行编码。而如果使用代码发送请求,那么就必须手动的进行编码,这时候就应该使用 urlencode 函数来实现。 urlencode 可以把字典数据转换为 URL 编码的数据。示例代码如下 :

# 将查询字符串进行编码
params = {'name': '张三', 'age': 18, 'say': 'hello world'}
# 中文编码:%+十六进制
result = parse.urlencode(params)
print(result)
# 输出结果:name=%E5%BC%A0%E4%B8%89&age=18&say=hello+world

# 利用parse.urlencode 拼接url进行查询
url = 'http://www.baidu.com/s'
params1 = {'wd': '白敬亭'}
# 进行编码
qs = parse.urlencode(params1)
# 拼接url
url = url + '?' + qs
resp = request.urlopen(url)
#print(resp.read().decode('utf-8'))

prase.parse_qs 函数:

可以将经过编码后的 url 参数进行解码。示例代码如下:

param = {'wd': '白敬亭'}
# 进行编码
qs = parse.urlencode(param)
print(qs)  # wd=%E7%99%BD%E6%95%AC%E4%BA%AD
# 进行解码
pars = parse.parse_qs(qs)  
print(pars)  # {'wd': ['白敬亭']}

parse.urlparse 和 parse.urlsplit 函数:

有时候拿到一个 url,想要对这个 url 中的各个组成部分进行分割,那么这时候就可以使用urlparse 或者是 urlsplit 来进行分割。示例代码如下:

from urllib import parse
'''parse.urlparse '''
url = 'http://www.baidu.com/s;hello?wd=python&username=zh#1'
result = parse.urlparse(url)
# print(result)
# 获取某一个属性
print('scheme:', result.scheme)
print('netloc:', result.netloc)
print('path:', result.path)
print('params:', result.params)
print('query:', result.query)
print('fragment', result.fragment)
'''parse.urlsplit '''
result = parse.urlsplit(url)
# 获取某一个属性
print('scheme:', result.scheme)
print('netloc:', result.netloc)
print('path:', result.path)
# print('params:', result.params)
print('query:', result.query)
print('fragment', result.fragment)

urlparse 和 urlsplit 基本上是一模一样的。
唯一不一样的地方是, urlparse 里面多了一个 params 属性,而 urlsplit 没有这个 params 属性

ProxyHandler 处理器(代理设置)

很多网站会检测某一段时间某个 IP 的访问次数(通过流量统计,系统日志等),如果访问次数 多的不像正常人,它会禁止这个 IP 的访问。 所以我们可以设置一些代理服务器,每隔一段 时间换一个代理,就算 IP 被禁止,依然可以换个 IP 继续爬取。

常用的代理有:

  1. 西刺免费代理 IP: http://www.xicida
  2. 快代理: http://www.kuaidaili.com/
  3. 代理云: http://www.dailiyun.com/

代理的原理:在请求目的网页之前,先请求代理服务器,然后让代理服务器去请求目的网页,代理服务器拿到目的网页的数据后,再转发给代码

http://httpbin.org:这个网站可以的方便的查看http请求的一些参数

使用代理如示例:

print('不使用代理') 
url = 'http://httpbin.org/ip'
headers = {'User-Agent': "Mozilla/5.0 (Macintosh; U; Mac OS X Mach-O; "   
           "en-US; rv:2.0a) Gecko/20040614 Firefox/3.0.0 "}  
req = request.Request(url, headers=headers) 
resp = request.urlopen(req) 
print(resp.read().decode('utf-8'))  
# 使用代理 print('使用代理') 
# 1.使用proxyhandler,传入代理构建一个hanlder
proxies = {'https': 'https://136.228.129.36:41838'} 
handler = request.ProxyHandler(proxies) 
# 2.使用上面的hanlder构建一个opener
opener = request.build_opener(handler)
# 3.使用opener去发送一个请求 
resp1 = opener.open(url)
print(resp1.read().decode('utf-8'))

Cookie

什么是 cookie

在网站中, http 请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie 的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie) 给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的 cookie 数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。 cookie 存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过 4KB。因此使用 cookie 只能存储一些小量的数据。

cookie 的格式:
Set-Cookie: NAME=VALUE; Expires/Max-age=DATE; Path=PATH; Domain=DOMAIN_NAME;SECURE
参数意义:
NAME: cookie 的名字。
VALUE: cookie 的值。
Expires: cookie 的过期时间。
Path: cookie 作用的路径。
Domain: cookie 作用的域名。
SECURE:是否只在 https 协议下起作用。

使用 cookielib 库和 HTTPCookieProcessor 模拟登录

Cookie 是指网站服务器为了辨别用户身份和进行 Session 跟踪,而储存在用户浏览器上的文本文件, Cookie 可以保持登录信息到用户下次与服务器的会话。
这里以人人网为例。人人网中,要访问某个人的主页,必须先登录才能访问,登录说白了就是要有 cookie 信息。那么如果我们想要用代码的方式访问,就必须要有正确的 cookie 信息才能访问。解决方案有两种,第一种是使用浏览器访问,然后将 cookie 信息复制下来,放到 headers 中。示例代码如下:

'''1.使用浏览器访问,然后将cookie信息赋值下来,放到headers中'''
# 以豆瓣网为例:
hearders = {
    'Cookie': 'cookie'
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'
    url = 'https://www.douban.com/gallery/topic/321545/?from=hot_topic_homepage'
    req = request.Request(url, headers=hearders)
    resp = request.urlopen(req)
    with open('result.html', 'w', encoding='utf-8') as f:
    	f.writelines(resp.read().decode('utf-8'))
    	# 写入的数据类型为 str
    	# resp.read() 出来是一个bytes类型的数据,bytes-decode-str, str-encode-byte

但是每次在访问需要 cookie 的页面都要从浏览器中复制 cookie 比较麻烦。在 Python处理 Cookie,一般是通过 http.cookiejar 模块和 urllib 模块的 HTTPCookieProcessor 处理器类 一 起 使 用 。 http.cookiejar 模 块 主 要 作 用 是 提 供 用 于 存 储 cookie 的 对 象 。 而HTTPCookieProcessor 处理器主要作用是处理这些cookie 对象,并构建 handler 对象。

http.cookiejar 模块:
该模块主要的类有 CookieJar、 FileCookieJar、 MozillaCookieJar、 LWPCookieJar。这四个类的作用分别如下:
CookieJar:管理 HTTP cookie 值、存储 HTTP 请求生成的 cookie、向传出的 HTTP 请求添加 cookie 的对象。整个 cookie 都存储在内存中,对 CookieJar 实例进行垃圾回收后 cookie也将丢失。
FileCookieJar (filename,delayload=None,policy=None):从 CookieJar 派生而来,用来建 FileCookieJar 实例,检索 cookie 信息并将 cookie 存储到文件中。 filename 是存储 cookie的文件名。 delayload 为 True 时支持延迟访问访问文件,即只有在需要时才读取文件或在文件中存储数据。
MozillaCookieJar (filename,delayload=None,policy=None):从 FileCookieJar 派生而来,创建与 Mozilla 浏览器 cookies.txt 兼容的 FileCookieJar 实例。
LWPCookieJar (filename,delayload=None,policy=None):从 FileCookieJar 派生而来,创建与 libwww-perl 标准的 Set-Cookie3 文件格式兼容的 FileCookieJar 实例

header = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/75.0.3770.100 Safari/537.36',
}
def get_opener():
	# 1.1创建一个cookiejar对象
	cookiejar = CookieJar()
	# 1.2使用cookiejar创建一个HTTPCookieProcessor对象
	handler = request.HTTPCookieProcessor(cookiejar)
	# 1.3使用上一步创建的handler创建一个opener
	opener = request.build_opener(handler)
    return opener
def login_renren(opener):
    # 使用构建的 opener 访问登录页面
    url = 'http://www.renren.com/PLogin.do'
    data = { 'email': '15666809216',  'password': 'beauty234'}
    param_parse = urlencode(data)  # str
    req = request.Request(url, data=param_parse.encode('utf-8'), headers=header)
    res = opener.open(req)
    return res
def visit_home(opener):
    # 访问某人主页
    dapeng_url = 'http://www.renren.com/880151247/profile'
    # 不用新建opener,因为之前的已经包含了登录所需要的cookie信息
    resp = request.Request(dapeng_url, headers=header)
    req2 = opener.open(resp)
    with open('renren3.html', 'w', encoding='utf-8') as f:
        f.write(req2.read().decode('utf-8'))

if __name__ == 'main':
    opener = get_opener()
    login_renren(opener)
    visit_home(opener)

保存及加载cookie

from urllib import request
from http.cookiejar import MozillaCookieJar

# 保存cookie信息
cookiejar = MozillaCookieJar('cookie.txt')  # 指定文件名
handler = request.HTTPCookieProcessor(cookiejar)
opener = request.build_opener(handler)
# 保存httpbin的cookie信息
resp = opener.open('http://www.httpbin.org/cookies/set?course=spider')
# ignore_discard 保存过期的cookie信息
cookiejar.save(ignore_discard=True)

# 加载cookie信息
cookiejar.load(ignore_discard=True)
for cookie in cookiejar:
    print(cookie)

Requests 库

虽然 Python 的标准库中 urllib 模块已经包含了平常我们使用的大多数功能,但是它的 API使用起来让人感觉不太好,而 Requests 宣传是 “HTTP for Humans”,说明使用更简洁方便。

安装和文档地址:

利用 pip 可以非常方便的安装:
pip install requests (conda install requests)
中文文档: http://docs.python-requests.org/zh_CN/lat
github 地址: https://github.com/requests/requests

发送get请求

  1. 最简单的发送 get 请求就是通过 requests.get 来调用:
    response = requests.get(“http://www.baidu.com/”)
  2. 添加 headers 和查询参数 params:
    如果想添加 headers,可以传入 headers 参数来增加请求头中的 headers 信息。如果要将参
    数放在 url 中传递,可以利用 params 参数。相关示例代码如下:
import requests

response = requests.get('http://www.baidu.com/')
print(type(response.text))  # str(Unicode字符串)
print(response.text)  # 可能会产生乱码
print(type(response.content))  # bytes
print(response.content.decode('utf-8'))
# response.content 直接从网络上抓取的数据,没有经过任何的解码,所以是一个bytes类型
# 其实在硬盘上和网络上传输的字符串都是bytes类型
# response.text 将response.content 进行解码的字符串,解码需要指定一个编码方式
# requests会根据自己的猜测判断编码方式,所以有时候会猜测错误,导致解码产生问题,需要使用
# response.content.decode()手动解码
# 当前url
print(response.url)
# 当前编码方式
print(response.encoding)
# 响应状态码
print(response.status_code)
# 如果网页返回的是 json,那么可以通过 response.json()转换为 dict 或者 list
# print(response.json())
# 传递参数
params = {  
    'wd': 'python'
}
# params 是一个字典或者字符串的查询参数,字典自动转换为 url 编码,不需要
# urlencode()
header = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/75.0.3770.100 Safari/537.36'
}
response = requests.get('http://www.baidu.com/s', params=params, headers=header)
print(response.url)
with open('baidu1.html', 'w', encoding='utf-8') as fp:
    fp.write(response.content.decode('utf-8'))

发送 POST 请求:

  1. 最基本的 POST 请求可以使用 post 方法:
    response = requests.post(“http://www.baidu.com/”,data=data)
  2. 如果网页返回的是 json,那么可以通过 response.json()转换为 dict 或者 list
  3. 传入 data 数据:
    这时候就不要再使用 urlencode 进行编码了,直接传入一个字典进去就可以了。
    比如请求拉勾网的数据的代码:
import requests
url = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'
data = {
    'first': 'true',
    'pn':  1,
    'kd':  'python'
}
header = {
    'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',
    'Accept': 'application/json, text/javascript, */*; q=0.01',
    'Cookie': 'user_trace_token=xxx'
}
response1 = requests.post(url, data=data, headers=header)
response1.encoding = 'UTF-8'
print(response1.text)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值