文章目录
爬虫
爬虫的概念
爬虫是模拟浏览器发送请求,获取响应
爬虫的定义
网络爬虫(又称为网页蜘蛛,网络机器人)就是模拟客户端发送网络请求,接收请求相应,一种按照一定的规则,自动地抓取换联网信息的程序。
只要是浏览器能做的事情,原则上,爬虫都能做。
爬虫的流程
1.url–>发送请求获取响应—>提取数据–>保存
2.发送请求,获取响应---->提取url
import json
t = json.loads("{}")
URL的形式
形式: scheme://host[:port#]/path/…/[?query-string][#anchor]
scheme:协议(例如:http,https,ftp)
host:服务器的IP地址或者域名
port:服务器的端口(如果走协议默认端口,80 or 443)
path:访问资源的路径
query-string:参数,发送给http服务器的数据
anchor:锚/主播名(跳转到网页的指定锚点位置)
HTTP的请求形式
以百度网站为例:
Connection: keep-alive (常链接),避免三次挥手四次握手,提高浏览器打开的效率
用户代理分析:(或者称为浏览器使用的标识)
Mozilla/5.0:浏览器的 内核版本
(Windows NT 10.0; Win64; x64) : 电脑的信息
AppleWebKit/537.36: 苹果浏览器的内核版本名字和版本号
Chrome/90.0.4430.93:谷歌浏览器的内核版本和版本号
每个浏览器都有自己的内核版本和版本号,这导致网页打开的效果是不一样的。
我们来做一个简单的实验:
由切换设备工具栏来在电脑上打开一个手机的浏览界面。(左上角可知我们切换到的是iphone 5/5se)
这时的用户代理:
此时的浏览器内核版本变成了:iPhone; CPU iPhone OS 10_3_1 like Mac OS X
问题: 为什么网页url都是一样的,显示却不是同一样的内容
回答: 通过用户代理,来模仿或伪装成一个手机浏览器,我们只需要把useragent变成手机版的就可以了
Upgrade-Insecure-Requests: 告诉浏览器支持将一些不安全的请求转换成安全的请求—>http转换成https
Accept-Encoding: 表示浏览器告诉服务端要接受什么样的数据压缩格式(压缩:减小数据体积,提高效率)
Accept-Language: 可以接收什么样的数据(q = ??表示接收的权重)
Cookie:保存在浏览器本地的用户数据(不安全,有存储上限)
反爬虫和反反爬虫都用Cookie来解决: 因为爬虫并不会启用网站下的Cookie
常见的请求方法
GET:把参数包含在URl中,一般都用GET请求
POST:适合在数据比较大的时候,提交表单,图片,翻译(数据比较多的大文本)
响应状态码(status code)
200:成功
302:临时转移至新的url
307:临时转移至新的url
404:not found
500:服务器内部错误
爬虫的分类
通用爬虫:通常是指搜索引擎的爬虫
聚焦爬虫:针对特定网站的爬虫
通用搜索引擎的工作原理:
想一想:
如果自己要实现一个和百度新闻一样的网站需要怎么做?
替换数据库里的数据为新闻。写爬虫爬取新闻。发送请求,得到url,提取数据。
为什么会有从响应中提取url地址: 只能拿到标题和标题地址,要获取新闻内容需要重新爬取,发送一个请求。(也就是重新爬)
爬虫的更多用途
12306抢票
网站上的投票
短信轰炸
搜索引擎的工作原理及robots协议
网站点击数、相关性
pagerank算法
通用搜索引擎的局限性:
通用搜索引擎所返回的网页里90%的内容无用
图片、音频、视频多媒体的内容通用搜索引擎无能为力
不同用户搜索的目的不同,但是返回内容相同
robots协议:网页通过robors协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。(道德层面的约束,无法真正阻止)
例如:(淘宝)https://www.taobao.com/robots.txt
图中百度遵循了淘宝的robots协议(也可能没遵守,爬了没显示,哈哈哈 )
浏览器发送HTTP请求的过程
爬虫要根据当前url地址对应的相应为准,当前url地址的elements的内容和url的响应不一样,因为js可能会调整页面的内容
页面上的数据在哪里呢?
当前url地址对应的响应中
其他的url地址对应的响应中,例如ajax请求中(局部更新页面)
js生成的,第一种是部分数据在响应中,第二种是全部通过js生成的
发送简单的请求
需求: 通过requests向百度首页发送请求,获取百度首页的数据
response = requests.get(url)
response的常用方法:
response.text
response.content
response.status_code 获取状态码
response.requests.headers
response.hesders
import requests
response = requests.get("http://www.baidu.com")
print(response.status_code) #打印状态码
print(response.headers) #响应头信息
print(response.request.headers) #请求头,没有user-Agent的情况下requsets模块会自动生成一个
print(response.request.url)
print(response.url)
print(response.content.decode()) #由于没设置用于代理,该部分内容很少,容易被识别出为网络爬虫
运行结果:(不是200就失败了,是200有可能请求成功,只代表请求某一个url地址成功,而不是我们想要的url地址成功了)
判断请求是否成功
可以使用断言判断:(r如果成功返回200则不会报错)
assert:布尔类型,条件为真则断言成功,为假断言失败
assert response.status_code==200
发送带header的请求
为什么请求需要带上header?
模拟浏览器,欺骗服务器,获取和浏览器内容一致的内容
header的形式:字典
用法:requests.geturl(url,headers = headers)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"}
response = requests.get("http://www.baidu.com",headers=headers)
print(response.content.decode())
运行效果:和网页代码量基本一致
tip:若是只带上user-Agent还是不能够欺骗服务器,我们可以多带上几个参数,甚至带上get-cookies
发送带参数的请求
什么叫做请求参数:
例一:http://www.webkaka.com/tutorial/server/2015/021013/ (×)
例二:https://www.baidu.com/s?wd=python&c=b
关键的参数:‘wd’
参数的形式:字典
用法:requests.get(url,params=kw)
import requests
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"}
p = {'wd':'python'}
url_temp = "https://ww.baidu.com/s?"   #问号可有可无
r = requests.get(url_temp,headers = headers,params=p)
print(r.status_code)
print(r.request.url) #打印经过url编码的url地址
运行效果:(可以通过在线url解码或得原来的url地址)
另一种方式,url字符创格式化:(format)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"}
url="https://ww.baidu.com/s?wd={}".format('知乎')
r = requests.get(url)
print(r.status_code)
print(r.request.url)
动手尝试
实现认识贴吧的爬虫,保存前一千页到本地
思路:找到url地址的构造规律,循环实现爬取,传入贴吧名字(以百度贴吧为例)
#coding: utf-8
import requests
class Spider():
def __init__(self,name):
self.name = name
self.url_temp = "https://tieba.baidu.com/f?kw="+name+"&ie=utf-8&pn={}"
self.headers ={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"}
def get_url(self):
return [self.url_temp.format(i*50) for i in range(1000)]
def get_decode(self,url):
print(url)
response = requests.get(url,headers = self.headers)
return response.content.decode()
def save(self,html_str,page):
file_path = "{}--第{}页.html".format(self.name,page)
with open(file_path,"w",encoding="utf-8") as f:
f.write(html_str)
def run(self):
#构造url列表
url_list = self.get_url()
#遍历发送请求,获取响应
for x in url_list:
html_str = self.get_decode(x)
#保存入文件
self.save(html_str,url_list.index(x)+1)
if __name__ == '__main__':
tieba_name = input("输入想要获取的贴吧名:")
spider = Spider(tieba_name)
spider.run()
运行效果:(部分)