网络爬虫的定义
网络爬虫是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。
根据使用场景,网络爬虫可分为通用爬虫和聚焦爬虫两种。
通用爬虫
通用网络爬虫
是捜索引擎抓取系统(Baidu、Google、Yahoo等)的重要组成部分。主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。
通用搜索引擎(Search Engine)工作原理
- 什么是搜索引擎?
搜索引擎(Search
Engine)是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。
通用网络爬虫:从互联网中搜集网页,采集信息,这些网页信息用于为搜索引擎建立索引从而提供支持,它决定着整个引擎系统的内容是否丰富,信息是否即时,因此其性能的优劣直接影响着搜索引擎的效果。
- 第一步:抓取网页
搜索引擎网络爬虫的基本工作流程如下:
首先选取一部分的种子URL,将这些URL放入待抓取URL队列;
取出待抓取URL,解析DNS得到主机的IP,并将URL对应的网页下载下来,存储进已下载网页库中,并且将这些URL放进已抓取URL队列。
分析已抓取URL队列中的URL,分析其中的其他URL,并且将URL放入待抓取URL队列,从而进入下一个循环…
搜索引擎如何获取一个新网站的URL:
- 新网站向搜索引擎主动提交网址:(如http://zhanzhang.baidu.com/linksubmit/url)
- 在其他网站上设置新网站外链(尽可能处于搜索引擎爬虫爬取范围)
- 搜索引擎和DNS解析服务商(如DNSPod等)合作,新网站域名将被迅速抓取。
但是搜索引擎蜘蛛的爬行是被输入了一定的规则的,它需要遵从一些命令或文件的内容,如标注为nofollow的链接,或者是Robots协议。
Robots协议(也叫爬虫协议、机器人协议等),全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,例如:
淘宝网:https://www.taobao.com/robots.txt
腾讯网: http://www.qq.com/robots.txt
- 第二步:数据存储
搜索引擎通过爬虫爬取到的网页,将数据存入原始页面数据库。其中的页面数据与用户浏览器得到的HTML是完全一样的。
搜索引擎蜘蛛在抓取页面时,也做一定的重复内容检测,一旦遇到访问权重很低的网站上有大量抄袭、采集或者复制的内容,很可能就不再爬行。
-
第三步:预处理
搜索引擎将爬虫抓取回来的页面,进行各种步骤的预处理。
提取文字
中文分词
消除噪音(比如版权声明文字、导航条、广告等……)
索引处理
链接关系计算
特殊文件处理
…
除了HTML文件外,搜索引擎通常还能抓取和索引以文字为基础的多种文件类型,如 PDF、Word、WPS、XLS、PPT、TXT 文件等。我们在搜索结果中也经常会看到这些文件类型。
但搜索引擎还不能处理图片、视频、Flash 这类非文字内容,也不能执行脚本和程序。 -
第四步:提供检索服务,网站排名
搜索引擎在对信息进行组织和处理后,为用户提供关键字检索服务,将用户检索相关的信息展示给用户。
同时会根据页面的PageRank值(链接的访问量排名)来进行网站排名,这样Rank值高的网站在搜索结果中会排名较前,当然也可以直接使用 Money 购买搜索引擎网站排名,简单粗暴。
但是,这些通用性搜索引擎也存在着一定的局限性:
1.通用搜索引擎所返回的结果都是网页,而大多情况下,网页里90%的内容对用户来说都是无用的。
2.不同领域、不同背景的用户往往具有不同的检索目的和需求,搜索引擎无法提供针对具体某个用户的搜索结果。
3.万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎对这些文件无能为力,不能很好地发现和获取。
4.通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询,无法准确理解用户的具体需求。
聚焦爬虫
聚焦爬虫,是"面向特定主题需求"的一种网络爬虫程序,它与通用搜索引擎爬虫的区别在于:
聚焦爬虫在实施网页抓取时会对内容进行处理筛选,尽量保证只抓取与需求相关的网页信息。
网络爬虫抓取过程可以理解为模拟浏览器操作的过程。浏览器的主要功能是向服务器发出请求,在浏览器窗口中展示您选择的网络资源,HTTP是一套计算机通过网络进行通信的规则。
requests
import requests
# 1、定义起始url 需要爬取的url
base_url = "https://www.baidu.com/more/"
# 2、发起请求 接收相应信息 返回的是200的相应 表示请求正常
response = requests.get(base_url)
# 3、获取爬取到的文本信息 是字符串
# html = response.text
# print(html)
# 4、获取网页内容 content返回的是 bytes类型
# 然后使用decode进行解码成字符串
html = response.content.decode("utf-8")
print(html)
# 5、存储页面数据
with open("baidu.html", "w", encoding="utf-8") as f:
f.write(html)
携带参数的第一种方式
import requests
from urllib import parse
# 问号后面的都是参数
base_url = "https://search.sina.com.cn/?"
# 定义参数字典
params = {
"q": "nba",
"c": "news",
"from": "channel",
"ie": "utf-8"
}
# 将字典转换url编码格式
params_url = parse.urlencode(params)
# print(params_url)
# 拼接完整的url
full_url = base_url + params_url
# print(full_url)
# 对完整的url发起请求
response = requests.get(full_url)
html = response.text
print(html)
'''
总结:
1、找到爬取数据的网页url
在网页中点击右键检查,点击network,点击all,左上角刷新网页,然后数据就会出来
2、点击第一个网页信息
右边的response是相应的页面信息主要是html,headers是发起请求需要携带的数据
headers中:
(1)request url 是网页请求的url
(2)response headers 是相应的结果参数
(3)request headers 是发起请求需要携带的东西
(4)string params 是get请求携带的参数
3、携带参数 以及request url 使用requests发起请求 格式见本文件
'''
携带参数的第二种方式
import requests
# 问号后面的都是参数
base_url = "https://search.sina.com.cn/?"
# 定义参数字典
params = {
"q": "nba",
"c": "news",
"from": "channel",
"ie": "utf-8"
}
# 将参数使用params接收
response = requests.get(base_url, params=params)
html = response.text
print(html) # 查看请求的html
print(response.url) # 查看请求的url
携带参数的第三种方式
import requests
from urllib import parse
# 问号后面的都是参数
base_url = "https://search.sina.com.cn/?q=A%B9%C9&range=all&c=news&sort=time"
# 对完整的url发起请求
response = requests.get(base_url)
html = response.text
print(html) # 查看请求的html
print(response.url) # 查看请求的url
'''
总结:
一般使用的是后两种方式发起请求,方便简洁
如果携带的参数很多,则建议使用第二种方式
'''
注意:
dict1 = {"k": "python", "v": "蟒蛇"}
# 当代码出现 获取不到的情况 你又不想终止你的代码运行 可以使用get
print(dict1.get("kw")) # 没有获取到 返回None
# 当必须一定要 有kw的时候 就要用["kw"] 没有它程序不能 运行
# print(dict1["kw"]) # 没有找到 报 KeyError: 'kw'
response中的方法
# 发起请求 添加headers 请求头 让服务器知道是浏览器访问
response = requests.get(base_url, params=params, headers=headers)
print(response.text) # 获取字符串文本信息
print(response.content) # 获取字节流 bytes类型
print(response.url) # 获取请求的url
print(response.encoding) # 获取编码
print(response.status_code) # 获取状态码
print(response.headers) # 获取响应头
"""
总结:
携带参数的请求,如果说获取不到指定的页面数据,则需要加上请求头
第一步:请求头中添加User_Agent, headers 是一个字典,添加完之后
看看能不能获取到指定的数据,如果不能则需要将所有的请求头添加到headers
"""
post请求
# 注意: 一般情况下post请求的url和网页的url不一致
base_url = "https://fanyi.baidu.com/sug"
# 定义携带的参数
data = {"kw": kw}
# 发起post请求 data=None, json=None,
response = requests.post(base_url, data=data)
json
'''
json.loads: 将字符串转换为json数据(字典,列表)
json.dumps: 将json数据转换为字符串
json.cn: 格式化字符串 必须是字符串,不能是其他类型
'''
获取json数据
response= requests.post(base_url, data=data).content.decode("utf-8")
#方式一
# 将字符串转换为json数据 此时这里是字典
json_data = json.loads(response)
#方式二
# 获取json数据
json_data = response.json()
补充
list1 = []
list2 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for i in list2:
list1 += i
print(list1)
'''
总结:
列表相加 可以使用+=号, city_list += city_list1
判断某个信息 时候成功获取到,接口返回的信息 会告诉你,只有在获取到信息之后
我们才对指定 信息进行获取 ,如果不这样的话 会报错,影响代码正常运行,
此时应该写异常处理
'''
解密
import requests
import time
import random
import hashlib
def md5(value):
# 创建MD5对象
md5_obj = hashlib.md5()
# 加密字符串
md5_obj.update(bytes(value, encoding="utf-8"))
# 进行16位的加密
sign = md5_obj.hexdigest()
return sign
def youdao(i):
# 获取salt
salt = str(int(time.time() * 1000)) + str(random.randint(0, 9))
# print(salt)
# 获取sign
sign1 = "fanyideskweb" + i + salt + "@6f#X3=cCuncYssPsuRUE"
sign = md5(sign1)
# 定义data参数
data = {
"i": i,
# "from": "AUTO",
# "to": "AUTO",
# "smartresult": "dict",
"client": "fanyideskweb",
"salt": salt,
"sign": sign,
# "ts": "1558514897639",
# "bv": "cf156b581152bd0b259b90070b1120e6",
# "doctype": "json",
# "version": "2.1",
"keyfrom": "fanyi.web",
# "action": "FY_BY_REALTlME"
}
# 加上请求头 浏览器信息
headers = {
# "Accept": "application/json, text/javascript, */*; q=0.01",
# "Accept-Encoding": "gzip, deflate",
# "Accept-Language": "zh-CN,zh;q=0.9",
# "Connection": "keep-alive",
# "Content-Length": "238",
# "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "OUTFOX_SEARCH_USER_ID=1007772075@10.168.8.76; OUTFOX_SEARCH_USER_ID_NCOO=1844201936.6123636; _ga=GA1.2.1939912746.1552966532; JSESSIONID=aaaB9UfpkFL02gnEynoRw; ___rl__test__cookies=1558514897636",
# "Host": "fanyi.youdao.com",
# "Origin": "http://fanyi.youdao.com",
"Referer": "http://fanyi.youdao.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36",
# "X-Requested-With": "XMLHttpRequest"
}
# 定义起始url
base_url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
# 发送请求
response = requests.post(base_url, data=data, headers=headers)
# 获取response里面的json数据
json_data = response.json()
print(json_data)
print(type(json_data))
if __name__ == '__main__':
i = input("请输入需要翻译的内容:")
# i = "banana"
youdao(i)
"""
遇到的问题1:
""只携带参数data 发起请求的时候,请求不到数据,出现{'errorCode'"": ""50},",",
此时的解决方案是:加上请求头浏览器信息 再次发起请求
""问题2:还是获取不到信息 {'errorCode'"": ""50}",",
解决的方案是:把所有的请求头信息添加到headers中
"""
# i: banana
# client: fanyideskweb
# salt: 15585168560444
# sign: da50e3193cda496e1455ff28c1bb21b1
# keyfrom: fanyi.web
#
# i: apple
# "client": "fanyideskweb",
# "salt": "15585148976393",
# "sign": "147950af9758d1e79aeaacd4ff27d14d",
# "keyfrom": "fanyi.web",
#
#
# salt: 需要看一下 是否需要加密
# sign: 也要看一下是否需要加密
# 首要解决的问题是salt和sign生成的过程
'''
salt = o.salt = i = r + parseInt(10 * Math.random(), 10)
"" + (new Date).getTime() + parseInt(10 * Math.random(), 10) js
= "" + int(time.time() * 1000) + random.randint(0, 9)
o = r.generateSaltSign(t) = r(t)
r.generateSaltSign(t) = t.generateSaltSign(t) = r(t)
{
ts: r,
bv: t,
salt: i,
sign: n.md5("fanyideskweb" + e + i + "@6f#X3=cCuncYssPsuRUE")
}
e = t = "apple" 需要翻译的内容
var r = function(e)
{
var
t = n.md5(navigator.appVersion),
r = "" + (new Date).getTime(),
i = r + parseInt(10 * Math.random(), 10);
return {
ts: r,
bv: t,
salt: i,
sign: n.md5("fanyideskweb" + e + i + "@6f#X3=cCuncYssPsuRUE")
}
};
长度比较:
15585816225096 python
15585148976393 js
15585822104216
sign = o.sign
= n.md5("fanyideskweb" + e + i + "@6f#X3=cCuncYssPsuRUE")
= md5("fanyideskweb" + "apple" + salt + "@6f#X3=cCuncYssPsuRUE")
'''
Cookie
# 添加含有cookie的请求头
headers = {
"Cookie": "anonymid=junimtwwu50d24; _r01_=1; _de=5F71EE4FAB787F2D9124C42004FCABA0; depovince=GW; jebecookies=b450d427-3a0e-4aa8-8378-d3ada01f9f03|||||; JSESSIONID=abcmZXoIdRY6Weth6sJRw; ick_login=49d3b618-6b47-4a21-b233-16b3fc3afbef; p=d577c793658cfc4bf9ad91e52f20586a1; first_login_flag=1; ln_uact=18679030315; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=2f7dc309b51521affb888e36a312f0891; societyguester=2f7dc309b51521affb888e36a312f0891; id=964508591; xnsid=1cb3fa4a; ver=7.0; loginfrom=null; jebe_key=a56cca4b-9248-423e-848b-5eebe8ee1750%7C8562fb69c05d6f5ab11983a626d37548%7C1558591573088%7C1%7C1558591573577; wp_fold=0"
}
# 携带cookie发起请求
response = requests.get(base_url, headers=headers)
'''
分析:携带cookie可以获取到想要的信息,这是因为cookie中含有登录的信息,
服务器会接收登录信息 然后进行登录操作,保存登录状态,然后可以获取指定的个人网页信息
'''
# 获取cookie返回的是 cookiejar类型
cookiejar = response.cookies
# 将cookies转换为字典的格式
cookie_dict = requests.utils.dict_from_cookiejar(cookiejar)
Session
import requests
# 1、创建session对象
ssion = requests.session()
# 2、创建需要登录的信息字典
data = {
"email": "aaaaaaa",
"password": "bbbbbbb"
}
# 3、使用ssion进行post登录 需要找到登录的接口
# 得到的是含有登录信息的ssion
ssion.post("http://www.renren.com/PLogin.do", data=data)
# 4、使用含有登录的信息的ssion 进行获取个人网页信息
response = ssion.get("http://www.renren.com/964508591/profile")
# 5、获取个人信息
html = response.text
print(html)
IP代理
import requests
# 定义一个代理字典
proxies = {
# "http": "http://112.85.128.110:9999",
"https": "http://61.142.72.154:30074"
}
response = requests.get("https://www.baidu.com", proxies=proxies)
print(response)
print(response.text)
'''
http代理 : 可以访问https的url,因为http使用的是http的代理服务器
https代理: 拒绝访问https的url(可能访问成功,也可能不成功), 为了安全考虑,
很多人使用https的代理服务器,所以会强行关闭远程连接
'''