62.1-爬虫概述、Robots协议

2020-08-26  本文已影响0人  BeautifulSoulpy

好的生活就是不瞎想,做得多,要得少,常微笑懂知足!


总结:

  1. HTTP 是基于 socket 通讯的;是 异步请求;
  2. data参数决定是GET还是POST请求:为空采用GET方法;不为空采用POST方法;
  3. 对URL打包,对数据进行解析;

1. 概述

当今大数据的时代,网络爬虫已经成为了获取数据的一个重要手段。

爬虫,应该称为网络爬虫,也叫网页蜘蛛、网络机器人、网络蚂蚁等。
搜索引擎,就是网络爬虫的应用者。
为什么到了今天,反而这个词汇被频繁的提起呢?有搜索引擎不就够了吗?
实际上,大数据时代的到了,所有的企业都希望通过海量数据发现其中的价值。
所以,需要爬取对特定网站、特定类别的数据,而搜索引擎不能提供这样的功能,因此,需要自己开发爬虫来解
决。

2. 爬虫分类

通用爬虫

常见就是搜索引擎,无差别的收集数据、存储,提取关键字,构建索引库,给用户提供搜索接口。

爬取一般流程
1.初始一批URL, 将这些URL放到待爬取队列
2.从队列取出这些URL, 通过DNS解析IP, 对IP对应的站点下载HTML页面, 保存到本地服务器中, 爬取完的
URL放到已爬取队列。
3.分析这些网页内容, 找出网页里面的其他关心的URL链接, 继续执行第2步, 直到爬取条件结束。

搜索引擎如何获取一个新网站的URL
● 新网站主动提交给搜索引擎
● 通过其它网站页面中设置的外链
● 搜索引擎和DNS服务商合作, 获取最新收录的网站

聚焦爬虫

有针对性的编写特定领域数据的爬取程序,针对某些类别数据采集的爬虫,是面向主题的爬虫

3. Robots协议(原则)

指定一个robots.txt文件,告诉爬虫引擎什么可以爬取。

/ 表示网站根目录,表示网站所有目录。
Allow 允许爬取的目录
Disallow 禁止爬取的目录
可以使用通配符

淘宝 http://www.taobao.com/robots.txt
马蜂窝 http://www.mafengwo.cn/robots.txt

User-agent: Baiduspider
Allow: /article
Allow: /oshtml
Allow: /ershou
Disallow: /product/
Disallow: /

User-Agent: Googlebot
Allow: /article
Allow: /oshtml
Allow: /product
Allow: /spu
Allow: /dianpu
Allow: /oversea
Allow: /list
Allow: /ershou
Disallow: /

User-agent: Bingbot
Allow: /article
Allow: /oshtml
Allow: /product
Allow: /spu
Allow: /dianpu
Allow: /oversea
Allow: /list
Allow: /ershou
Disallow: /

User-agent: Baiduspider
Allow: /article
Allow: /oshtml
Allow: /ershou
Disallow: /product/
Disallow: /
其它爬虫,不允许爬取
User-Agent: *
Disallow: /

这是一个君子协定,“爬亦有道”

这个协议为了让搜索引擎更有效率搜索自己内容,提供了如Sitemap这样的文件。Sitemap往往是一个XML文件,
提供了网站想让大家爬取的内容的更新信息。
这个文件禁止抓取的往往又是可能我们感兴趣的内容,它反而泄露了这些地址。

301,302

301,302 都是HTTP状态的编码,都代表着某个URL发生了转移,不同之处在于:
301 redirect: 301 代表永久性转移(Permanently Moved)。
302 redirect: 302 代表暂时性转移(Temporarily Moved )。

4. HTTP请求和响应处理

其实爬取网页就是通过HTTP协议访问网页,不过通过浏览器访问往往是人的行为,把这种行为变成使用程序来访问。

4.1 urllib包

urllib包(最老的爬虫库)
urllib是标准库,它一个工具包模块,包含下面模块来处理url:
urllib.request 用于打开和读写url
urllib.error 包含了由urllib.request引起的异常
urllib.parse 用于解析url
urllib.robotparser 分析robots.txt 文件

Python2中提供了urllib和urllib2。urllib提供较为底层的接口,urllib2对urllib进行了进一步封装。Python3中将
urllib合并到了urllib2中,并更名为标准库urllib包。

1. urllib.request模块

模块定义了在基本和摘要式身份验证、 重定向、 cookies等应用中打开 Url (主要是 HTTP) 的函数和类。

urlopen方法
urlopen(url, data=None)
url是链接地址字符串,或请求类的实例。
data提交的数据,如果data为None发起GET请求,否则发起POST请求。见 urllib.request.Request#get_method
返回http.client.HTTPResponse类的响应对象,这是一个类文件对象。

from urllib import request
from http.client import HTTPResponse

url = "https://cn.bing.com/"  

response:HTTPResponse = request.urlopen(url)  # 类文件对象;

with response:
    print(1,type(response))     # 类文件对象
    print(2,response.headers)  # 等价 info ()   #  
    print(2,response.info())  # 等价 info ()    # 
    print(3,response.geturl())
    print(4,response.status,response.reason)     
    print(5,response.read())            # 二进制的bytes对象
#------------------------------------------------------------------------------
C:\ProgramData\Miniconda3\envs\blog\python.exe C:/Users/dell/PycharmProjects/spiders/t1.py
1 <class 'http.client.HTTPResponse'>
2 Cache-Control: private, max-age=0
Content-Length: 111838
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
P3P: CP="NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND"
Set-Cookie: SRCHD=AF=NOFORM; domain=.bing.com; expires=Fri, 26-Aug-2022 02:43:09 GMT; path=/
Set-Cookie: SRCHUID=V=2&GUID=ECC0C843F1BA4EE7A8D5557520066A17&dmnchg=1; domain=.bing.com; expires=Fri, 26-Aug-2022 02:43:09 GMT; path=/
Set-Cookie: SRCHUSR=DOB=20200826; domain=.bing.com; expires=Fri, 26-Aug-2022 02:43:09 GMT; path=/
Set-Cookie: _SS=SID=2FDBF51342FC674231D5FA2143D266D4; domain=.bing.com; path=/
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-MSEdge-Ref: Ref A: 1E707EB1AAB64DB69C0FD272FD61B7BD Ref B: BJ1EDGE0221 Ref C: 2020-08-26T02:43:09Z
Set-Cookie: _EDGE_S=F=1&SID=2FDBF51342FC674231D5FA2143D266D4; path=/; httponly; domain=bing.com
Set-Cookie: _EDGE_V=1; path=/; httponly; expires=Mon, 20-Sep-2021 02:43:09 GMT; domain=bing.com
Set-Cookie: MUID=3AC183D8072E6AED16758CEA06006BDE; samesite=none; path=/; secure; expires=Mon, 20-Sep-2021 02:43:09 GMT; domain=bing.com
Set-Cookie: MUIDB=3AC183D8072E6AED16758CEA06006BDE; path=/; httponly; expires=Mon, 20-Sep-2021 02:43:09 GMT
Date: Wed, 26 Aug 2020 02:43:08 GMT
Connection: close

上例,通过urllib.request.urlopen方法,发起一个HTTP的GET请求,WEB服务器返回了网页内容。响应的数据被
封装到类文件对象中,

通过read方法、readline方法、readlines方法获取数据,
status和reason属性表示返回的状态码,
info方法返回头信息,

User-Agent问题
上例的代码非常精简,即可以获得网站的响应数据。但目前urlopen方法通过url字符串和data发起HTTP的请求。
如果想修改HTTP头,例如useragent,就得借助其他方式。

userAgent

源码中构造的useragent如下

# urllib.request.OpenerDirector
class OpenerDirector:
def __init__(self):
client_version = "Python-urllib/%s" % __version__
self.addheaders = [('User-agent', client_version)]

当前显示为 Python-urllib/3.6
有些网站是反爬虫的,所以要把爬虫伪装成浏览器。随便打开一个浏览器,复制浏览器的UA值,用来伪装。

Request类

Request(url, data=None, headers={})
初始化方法,构造一个请求对象。可添加一个header的字典。data参数决定是GET还是POST请求
add_header(key, val) 为header中增加一个键值对。

from urllib.request import Request, urlopen
import random
# 打开一个url返回一个Request请求对象
# url = 'https://movie.douban.com/' # 注意尾部的斜杠一定要有
url = 'http://www.bing.com/'

ua_list = [
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",# chrome
"Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36", # safafi
]

ua=random.choice(ua_list) #pick one
#ua需要加到请求头中
request=Request(url)
request.add_header('User-Agent', random.choice(ua_list) )
print(type(request) )

response = urlopen(request, timeout=20) # request对象或者url都可以
print(type(response))

with response:
    print(1, response.status, response.getcode(), response.reason) # 状态,getcode本质上就是返回 status

    print(2, response.geturl()) # 返回数据的url。如果重定向,这个url和原始url不一样
    # 例如原始url是http://www.bing.com/,返回http://cn.bing.com/
    print(3, response.info()) # 返回响应头headers
    print(4, response.read()) # 读取返回的内容
print(5, request.get_header('User-agent'))
print(6, 'user-agent'.capitalize())
2.urllib.parse模块

该模块可以完成对url的编解码; 先看一段代码,进行编码(默认UTF-8)

from urllib import parse
#  urlencode函数第一参数要求是一个字典或者二元组序列
u = parse.urlencode({'url':'http://www.magedu.com/python'})
print(u)
#---------------------------------------------------
url=http%3A%2F%2Fwww.magedu.com%2Fpython

/  %2F
=  3D


从运行结果来看冒号、斜杠、&、等号、问号等符号全部被编码了,%之后实际上是单字节十六进制表示的值。

一般来说url中的地址部分,一般不需要使用中文路径,但是参数部分,不管GET还是POST方法,提交的数据中,
可能有斜杆、等号、问号等符号,这样这些字符表示数据,不表示元字符。如果直接发给服务器端,就会导致接收
方无法判断谁是元字符,谁是数据了。

为了安全,一般会将数据部分的字符做url编码,这样就不会有歧义了。
后来可以传送中文,同样会做编码,一般先按照字符集的encoding要求转换成字节序列,每一个字节对应的十六
进制字符串前加上百分号即可。

# 网页使用utf-8编码
# https://www.baidu.com/s?wd=中
# 上面的url编码后,如下
# https://www.baidu.com/s?wd=%E4%B8%AD
from urllib import parse
u = parse.urlencode({'wd':'中'}) # 编码
url = "https://www.baidu.com/s?{}".format(u)

print(url)
print('中'.encode('utf-8')) # b'\xe4\xb8\xad'
print(parse.unquote(u)) # 解码
print(parse.unquote(url))
------------------------------------------------------
https://www.baidu.com/s?wd=%E4%B8%AD
b'\xe4\xb8\xad'
wd=中
https://www.baidu.com/s?wd=中
4.2 提交方法method

最常用的HTTP交互数据的方法是GET、POST。
GET方法,数据是通过URL传递的,也就是说数据是在HTTP报文的header部分。
POST方法,数据是放在HTTP报文的body部分提交的。
数据都是键值对形式,多个参数之间使用&符号连接。例如a=1&b=abc

from urllib import request,parse
from http.client import HTTPResponse

# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"

base_url = "https://cn.bing.com/search"

u = parse.urlencode({'q':'马哥教育'})
url = "{}?{}".format(base_url,u)

req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})

with request.urlopen(req) as response:
    with open('./bing.html','wb') as f:   #注意编码;
        f.write(response.read())
#-------------------------------------------------------

GET方法

连接 必应 搜索引擎官网,获取一个搜索的URL http://cn.bing.com/search?q=马哥教育
需求 : 请写程序完成对关键字的bing搜索, 将返回的结果保存到一个网页文件

from urllib import request,parse
from http.client import HTTPResponse

# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
# 请求什么返回什么
url = "http://httpbin.org/get"

req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})

with request.urlopen(req) as response:
    print(response.read())
#------------------------------------------------------------
b'{\n  "args": {}, \n  "headers": {\n    "Accept-Encoding": "identity", \n    "Host": "httpbin.org", \n    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", \n    "X-Amzn-Trace-Id": "Root=1-5f463de0-28f2bea1625061d03c4977d3"\n  }, \n  "origin": "117.150.188.202", \n  "url": "http://httpbin.org/get"\n}\n'

POST方法

http://httpbin.org/ 测试网站

from urllib import request,parse
from http.client import HTTPResponse

# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
# 请求什么返回什么
url = "http://httpbin.org/post"

req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})

params = parse.urlencode({
    'id':100,
    'name':'tommy'
})
with request.urlopen(req,params.encode()) as response:
    print(response.read())
#- --------------------------------------------------------------
b'{\n  "args": {}, \n  "headers": {\n    "Accept-Encoding": "identity", \n    "Host": "httpbin.org", \n    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", \n    "X-Amzn-Trace-Id": "Root=1-5f463de0-28f2bea1625061d03c4977d3"\n  }, \n  "origin": "117.150.188.202", \n  "url": "http://httpbin.org/get"\n}\n'
处理JSON数据

查看"豆瓣电影",看到"最近热门电影"的“热门” ;

热门

XHR : xml http request
tag 标签“热门”,表示热门电影
type 数据类型,movie是电影
page_limit 表示返回数据的总数
page_start 表示数据偏移

通过分析,我们知道这部分内容,是通过A JAX从后台拿到的Json数据。
访问URL是https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_limit=50&page_start=0%E7%83%AD%E9%97%A8
是utf-8编码的中文“热门” 服务器返回的Json数据50条

from urllib import request,parse
from http.client import HTTPResponse

# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
# 请求什么返回什么
url = "https://movie.douban.com/j/search_subjects"
params = parse.urlencode({
    'type':'movie',
    'tag':'热门',
    'page_limit':3,
    'page_start':5,
})

url += '?' + params
req = request.Request(url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})

with request.urlopen(req) as response:
    print(response.read())
-----------------------------------------------
b'{"subjects":[{"rate":"8.3","cover_x":5906,"title":"\xe5\xb0\x91\xe5\xb9\xb4\xe7\x9a\x84\xe4\xbd\xa0","url":"https:\\/\\/movie.douban.com\\/subject\\/30166972\\/","playable":true,"cover":"https://img3.doubanio.com\\/view\\/photo\\/s_ratio_poster\\/public\\/p2572166063.jpg","id":"30166972","cover_y":8268,"is_new":false},{"rate":"5.6","cover_x":1500,"title":"\xe8\xb6\x85\xe8\x83\xbd\xe8\xae\xa1\xe5\x88\x92","url":"https:\\/\\/movie.douban.com\\/subject\\/30330875\\/","playable":false,"cover":"https://img9.doubanio.com\\/view\\/photo\\/s_ratio_poster\\/public\\/p2614190404.jpg","id":"30330875","cover_y":2222,"is_new":false},{"rate":"6.2","cover_x":1080,"title":"\xe5\xa6\x99\xe5\x85\x88\xe7\x94\x9f","url":"https:\\/\\/movie.douban.com\\/subject\\/34888476\\/","playable":true,"cover":"https://img9.doubanio.com\\/view\\/photo\\/s_ratio_poster\\/public\\/p2614234255.jpg","id":"34888476","cover_y":1512,"is_new":false}]}'

上一篇下一篇

猜你喜欢

热点阅读