正文共: 10409字 6图 预计阅读时间: 27分钟
To understand and be understood, those are among life’s greatest gifts, and every interaction is an opportunity to exchange them.
理解与被理解,都是生活中最棒的礼物。每次的互动都是交换它们的一次机会。
小闫语录:
被误会,被责备,被嘲笑,在那一刻,你是多么的渴望被理解,犹如久旱逢甘霖一般。只有经历过的人才会明白被理解是多么的美好珍贵。其实还有一样东西与此同样重要,那便是理解。你渴望的同样也是其他人渴望的,去理解他人,换回的将是被理解。当与其他人发生矛盾的时候,尝试站到对方的角度去思考一下,许多矛盾都会迎刃而解。
环境:Windows10企业版,版本号1809;pycharm2017.3.3。
背景:在将爬取的数据执行写入文件操作时报错。
代码:
with open('baidu2.html','w') as f:
f.write(resp2.content.decode())
错误:UnicodeEncodeError: 'gbk' codec can't encode character '\xbb' in position 30633: illegal multibyte sequence。
解决办法:
with open('baidu2.html','w',encoding='utf-8') as f:
f.write(resp2.content.decode())
原因分析:在Windows中新建的文本文件默认编码是 gbk
,对于其他格式的数据无法编码,所以报错,我们可以指定编码格式为 utf-8
。
1.什么是Ip代理?
答:IP代理即代理服务器,其功能主要就是代理网络用户去获取网络信息,形象的说就是网络信息的中转站。
2.为什么爬虫需要使用代理?
答:让目标服务器以为不是同一个客户端在请求,防止因为ip发送请求过多而被反爬;防止我们的真实地址被泄漏;防止被追究责任。
3.怎么理解使用代理的过程?
答:代理ip是一个ip,指向的是一个代理服务器;代理服务器能够帮我们向目标服务器转发请求。
正向代理是保护客户端,反向代理是保护服务器。
免费IP代理的使用:生产环境下不能使用免费代理IP。
proxy = {
'http':'http://103.230.35.222:3128',
'https':'https://103.230.35.222:3128',
}
resp = requests.get(url,proxies=proxy)
生产环境下:ip池一般存入数据库,查询数据,随机选择ip来使用。而且需要维护一个ip池,ip池是付费和免费的IP混合使用。
付费IP的使用:user表示使用代理网站的账号,password表示账号密码。
proxy2 = {
'http':'http://user:password@103.230.35.222:3128',
'https':'https://user:password@103.230.35.222:3128',
}
注意:代理IP的协议,如果是HTTP,不能发送HTTPS的请求!!!
1.cookie数据存放在客户的浏览器上,session数据放在服务器上。
2.cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗(使用用户的cookies获取相关信息)。
3.session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器资源,降低性能。
4.单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
带上cookie和session的好处:很多网站必须登录之后(或者获取某种权限之后)才能够请求到相关数据。
带上cookie和session的弊端:一套cookie和session往往和一个用户对应,请求太快,请求次数太多,容易被服务器识别为爬虫。然后被封号,造成损失。
有些账号是很珍贵的,权限等级之类的,被封后损失很严重。
使用建议:
1.不需要cookie的时候尽量不去使用cookie,如必须要用,不要用自己的账号。
2.为了获取登录之后的页面,我们必须发送带有cookies的请求,此时为了确保账号安全应该尽量降低数据采集速度。
需求:获取人人网需要登录后,才能看到的页面。
cookie的使用第一种:在headers中传入cookie。
url = 'http://www.renren.com/438718956'
headers = {
# 从浏览器中复制过来的User-Agent
'User-Agent': '浏览器的用户代理',
# 从浏览器中复制过来的Cookie
'Cookie': 'xxx这里是复制过来的cookie字符串'
}
# 发送请求
resp = requests.get(url,headers=headers)
# 判断是否登录成功,可以判断响应的页面中是否有具有标识的特殊字段,此处账号名是`风雨`,我们可以看是否有风雨两字。
# 使用正则,从结果中匹配`风雨`
import re
print(re.findall('风雨',resp.content.decode()))
cookie的使用第二种:以字典的形式传入cookie信息。
cookie的本质是键值对形式的字符串。
url = 'http://www.renren.com/438718956'
headers = {
# 从浏览器中复制过来的User-Agent
'User-Agent': '浏览器的用户代理'
}
temp_str = 'xxx这里是复制过来的cookie字符串'
# 我们通过分析cookie字符串,发现里面的数据有规律,都是以等号连接的键值对,然后键值对用封号和空格隔开。因此我们用下列的操作进行提取cookie。
cookie = {}
for ck in temp_str.split('; '):
key = ck.split('=')[0]
value = ck.split('=')[-1]
cookie[key] = value
resp = requests.get(url,headers=headers,cookies=cookie)
使用cookie的弊端就是我们需要现在浏览器中登录,然后粘贴cookie信息,比较繁琐低效。
网址:http://www.renren.com/PLogin.do
session的使用:
1.实例化session对象。
2.使用session对象发送请求。
3.获取响应,解析响应数据。
import requests
url = 'http://www.renren.com/PLogin.do'
# 构造post请求的data数据
post_data = {
'email':'131****8225',
'password':'welcome to 小闫笔记'
}
s = requests.session()
resp = s.post(url,data=post_data)
import re
print(re.findall('风雨',resp.content.decode()))
session会帮我们自动实现状态保持。
1.cookiesjar与字典之间的相互转换
应用场景:在爬取某些网站的数据,cookie信息动态变化,如果cookie拿不到数据,可以使用 cookiejar
动态获取cookie信息再次发送网络请求。
把 cookiejar
对象转化为字典格式的cookies:
reqeusts.utils.dict_from_cookiejar
把字典格式的cookies转换成 cookiejar
对象:
requests.utils.cookiejar_from_dict
2.请求SSL证书验证
如果访问一个网站,遇到SSL错误信息,原因是该网站的CA认证证书不是标准的。
使用场景:Requests 可以为 HTTPS 请求验证 SSL 证书,就像 web 浏览器一样。SSL 验证默认是开启的,如果证书验证失败,Requests 会抛出 SSLError
使用方式:
response = requests.get("https://www.12306.cn/mormhweb/ ", verify=False)
3.设置超时
使用场景:有些站点或者代理反应慢,严重降低效率,这个时候可以设置超时。
使用方式:
response = requests.get(url,timeout=10)
timeout就是在这个时间过了之后,还是拿不到响应,那么就报错。 timeout:整型值,单位为s。
什么是数据提取?
答:简单的来说,数据提取就是从响应中获取我们目标数据的过程。
数据分类:
1.非结构化的数据:html,文本等。没有规律的。(此处的没有规律,举个例子来说,就是标签中有单个标签形式也会有成对标签的形式)
处理方法:正则表达式,xpath。
2.结构化数据:json,xml等。符合一定规律的。
处理方法:使用json模块,转化为python数据类型。
1.什么是json?
答:json是一种轻量级的数据交换格式,它使得人们很容易进行阅读和编写。同时也方便了机器进行解析和生产。适用于进行数据交互的场景,比如网站前台和后台之间的数据交互。
2.为什么要使用json?
答:把json格式字符串转换为python字典类型很简单,所以爬虫中,如果我们能够找到返回json数据格式字符串的url,就会尽量使用这种url。
3.如何找到返回json的url?
答:使用浏览器、抓包工具进行分析。
json在数据交换中起到了一个载体的作用,承载着相互传递的数据。json并不是一种数据类型。
json.dumps # 把字典转为json---操作的是变量
json.loads # 把json转成字典
json.dump # 把字典转为json---操作的是文件对象(具有read和write方法的对象)
json.load # 把json转成字典
需求:爬取豆瓣电视的电视名、基本信息、封面。
实现步骤:
1.构建请求信息。
2.发送请求,获取响应。
3.解析响应数据
4.保存数据。
技术点:使用json模块,结构化数据。
import json
import requests
class Douban:
def __init__(self):
self.url = 'https://m.douban.com/rexxar/api/v2/subject_collection/tv_korean/items?os=android&for_mobile=1&start={}&count=18'
self.start = 0
# 豆瓣反爬的手段:
self.headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Mobile Safari/537.36',
'Referer': 'https://m.douban.com/tv/korean'
}
self.file = open('douban.json','w')
def get_data(self,url):
resp = requests.get(url,headers=self.headers)
# print(resp.content.decode())
return resp.content.decode()
def parse_data(self,data):
# 解析json数据,转成字典
dict_data = json.loads(data)
# 提取字典中的电视数据列表
data_list = dict_data['subject_collection_items']
# 遍历数据列表,提取每个电视的数据
tv_list = []
for tv in data_list:
temp = {}
temp['title'] = tv['title']
temp['info'] = tv['info']
temp['url'] = tv['url']
tv_list.append(temp)
# print(tv_list)
return tv_list
def save_data(self,tv_list):
# 保存电视列表数据,遍历电视列表数据,把每条数据,转成json字符串,统一写入文件
for tv in tv_list:
json_str = json.dumps(tv,ensure_ascii=False) + ',\n'
self.file.write(json_str)
# 关闭文件
def __del__(self):
self.file.close()
def run(self):
while True:
# 1、构建请求信息
# 2、发送请求,获取响应
url = self.url.format(self.start)
data = self.get_data(url)
# 3、解析响应数据
tv_list = self.parse_data(data)
# 4、保存数据
self.save_data(tv_list)
# 5、运行
self.start += 18
# 定义循环的终止条件
if tv_list == []:
break
if __name__ == '__main__':
douban = Douban()
douban.run()
在前端中,看到155开头,13位左右的一串数,第一时间就要想到是否为时间戳。
总结:headers中的请求头信息,需要加入referer(从请求中查看的)。json模块的使用(dumps、loads可以用来提取数据,保存文件)。
需求:爬取36kr新闻网站的新闻数据,新闻标题、摘要、封面图片
步骤:
1.构建请求信息。
2.发送请求,获取响应。
3.解析响应数据。---正则。
4.保存数据。
5.运行。
案例中的注意点:
1.响应数据放在前端script标签的变量中。
2.使用正则提取后的json数据,有非json字符串。先把数据写文件,在文件中查找错误信息。提取错误信息,将错误过滤掉。
技术点:非结构化数据,页面的html标签中,使用re和json模块。
import json
import re
import requests
class Kr36:
def __init__(self):
self.url = 'https://36kr.com/'
self.headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
self.file = open('36kr.json','w')
def get_data(self):
resp = requests.get(self.url,headers=self.headers)
# print(resp.content.decode())
return resp.content.decode()
# 使用正则表达式,从script标签中提取数据
def parse_data(self,data):
# <script>var props=(.*?)</script>
result = re.findall('<script>var props=(.*?)</script>',data)[0]
# print(result)
json_data = result.split(',locationnal=')[0]
# with open('temp.json','w')as f:
# f.write(json_data)
dict_data = json.loads(json_data)
# print(dict_data)
data_list = dict_data['feedPostsLatest|post']
# 遍历数据列表,提取新闻数据
news_list = []
for news in data_list:
temp = {}
temp['title'] = news['title']
temp['summary'] = news['summary']
temp['cover'] = news['cover']
news_list.append(temp)
# print(news_list)
return news_list
def save_data(self,news_list):
for news in news_list:
json_str = json.dumps(news,ensure_ascii=False) + ',\n'
self.file.write(json_str)
def __del__(self):
self.file.close()
def run(self):
data = self.get_data()
news_list = self.parse_data(data)
self.save_data(news_list)
if __name__ == '__main__':
kr36 = Kr36()
kr36.run()
需求:爬取有道翻译结果,学习解析js代码。
是结构化数据
在浏览器中找到对应的js文件:
1.ctrl + F,查询关键字(一定是特殊的,不要找普通的,都具有的那种关键字)。
2.network中,找到对应的数据包,在后面找到initiator列中的js文件点进去。
3.根据标签对应的事件监听。
步骤:
1.构建请求信息。
2.发送请求,获取响应。
3.解析响应数据。
4.运行。
重点:
1.js解析的过程,根据需要查找的关键字进行js的解析。
2.有道翻译的反爬措施:请求头重必须要有referer和cookie,缺一不可。
import json
import random
import requests
# python中的哈希加密的模块
import hashlib
import time
"""
生成post请求的data数据:
r = "" + (new Date).getTime()
i = r + parseInt(10 * Math.random(), 10);
ts: r,
bv: t,
salt: i,
sign: n.md5("fanyideskweb" + e + i + "p09@Bn{h02_BIEe]$P^nG")
"""
class Youdao:
def __init__(self,kw):
self.url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
self.headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36',
'Referer': 'http://fanyi.youdao.com/',
'Cookie': 'OUTFOX_SEARCH_USER_ID=1677762632@10.169.0.83; JSESSIONID=aaalA_qEvNE-Tacc7zdLw; OUTFOX_SEARCH_USER_ID_NCOO=1283878255.2660573; ___rl__test__cookies=1551692784632'
}
self.kw = kw
self.post_data = None
def generate_post_data(self):
# r = "" + (newDate).getTime()
r = str(int(time.time() * 1000))
# i = r + parseInt(10 * Math.random(), 10);
i = r + str(random.randint(0,9))
ts = r
salt = i
# sign: n.md5("fanyideskweb" + e + i + "p09@Bn{h02_BIEe]$P^nG")
temp_str = "fanyideskweb" + self.kw + salt + "p09@Bn{h02_BIEe]$P^nG"
# 构造md5对象
md5 = hashlib.md5()
# 对要加密的字符串进行编码
md5.update(temp_str.encode())
# 转成16进制
sign = md5.hexdigest()
# 构造post请求的参数信息
self.post_data = {
'i': self.kw,
'from': 'AUTO',
'to': 'AUTO',
'smartresult': 'dict',
'client': 'fanyideskweb',
'salt': salt,
'sign': sign,
'ts': ts,
'bv': 'e5845ac95fe54fe7e094f141d5d0cc0d',
'doctype': 'json',
'version': '2.1',
'keyfrom': 'fanyi.web',
'action': 'FY_BY_REALTIME',
'typoResult': False
}
def get_data(self):
resp = requests.post(self.url,headers=self.headers,data=self.post_data)
# print(resp.content.decode())
return resp.content.decode()
def parse_data(self,data):
# print(data)
dict_data = json.loads(data)
result = dict_data['translateResult'][0][0]['tgt']
print(result)
def run(self):
self.generate_post_data()
data = self.get_data()
self.parse_data(data)
if __name__ == '__main__':
youdao = Youdao('人生苦短')
youdao.run()
1.锚点:url中出现#,那么此处内容就是锚点。作用:浏览器页面显示位置的操作。
2.url的构成:协议、主机、端口、路径、参数、锚点。
3.请求头:key-value形式。
4.xml数据:可扩展标记语言;作用是传输数据。
微信中的数据使用xml格式传输。
xmltodict模块:把xml数据转成字典,unparse/parse.
<xml>
<name> 小闫同学 </name>
<age>18</age>
</xml>
5.json格式数据:键值对形式的字符串;作用是传输数据。
{"name":"Ethanyan","age":18}
6.json:解决了不同平台、不同语言之间的数据交互。
<!--前端:对象-->
var parmas = {'name':'Ethanyan'....}
<!--把对象转成json-->
JSON.stringify(params)
<!--后端:字典-->
parmas = {'name':'Ethanyan'....}
<!--把字典转成json-->
json.dumps(params)
7.xml都是闭合标签;html中不一定,比如换行标签 <br>
。
8.正则表达式匹配ip地址:
# 最终版
(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})
# 其实是可以再简化的,重复 `点和数字` 三次就可以了
(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})(\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})){3}
# python3
import re
pattern = re.compile(r'(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})(\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})){3}')
str = ''
print(pattern.search(str))
9.正则表达式回忆:
代码 | 功能 |
---|---|
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
10.python中的哈希加密模块 hashlib
中有哈希256等加密算法。
11.random中的randint是闭区间。
12. md5.hexdigest()
将md5转为16进制。
13.html:超文本标记语言,作用是渲染数据。
优质文章推荐: