爬虫学习(二)

爬虫学习(二)

bug

环境: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代理

1.什么是Ip代理?

答:IP代理即代理服务器,其功能主要就是代理网络用户去获取网络信息,形象的说就是网络信息的中转站。

2.为什么爬虫需要使用代理?

答:让目标服务器以为不是同一个客户端在请求,防止因为ip发送请求过多而被反爬;防止我们的真实地址被泄漏;防止被追究责任。

3.怎么理解使用代理的过程?

答:代理ip是一个ip,指向的是一个代理服务器;代理服务器能够帮我们向目标服务器转发请求。

正向代理是保护客户端,反向代理是保护服务器。

1.1使用代理

免费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:[email protected]:3128', 'https':'https://user:[email protected]:3128', }

注意:代理IP的协议,如果是HTTP,不能发送HTTPS的请求!!!

2.cookie和session

2.1二者区别

1.cookie数据存放在客户的浏览器上,session数据放在服务器上。

2.cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗(使用用户的cookies获取相关信息)。

3.session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器资源,降低性能。

4.单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

2.2利弊与抉择

带上cookie和session的好处:很多网站必须登录之后(或者获取某种权限之后)才能够请求到相关数据。

带上cookie和session的弊端:一套cookie和session往往和一个用户对应,请求太快,请求次数太多,容易被服务器识别为爬虫。然后被封号,造成损失。

有些账号是很珍贵的,权限等级之类的,被封后损失很严重。

使用建议

1.不需要cookie的时候尽量不去使用cookie,如必须要用,不要用自己的账号。

2.为了获取登录之后的页面,我们必须发送带有cookies的请求,此时为了确保账号安全应该尽量降低数据采集速度。

2.3案例-使用cookie来获取登录之后人人网的响应

需求:获取人人网需要登录后,才能看到的页面。

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)

2.4案例-使用session来登录人人网

使用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会帮我们自动实现状态保持。

2.5requests小技巧

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。

3.数据提取

什么是数据提取?

答:简单的来说,数据提取就是从响应中获取我们目标数据的过程。

数据分类

1.非结构化的数据:html,文本等。没有规律的。(此处的没有规律,举个例子来说,就是标签中有单个标签形式也会有成对标签的形式)

处理方法:正则表达式,xpath。

2.结构化数据:json,xml等。符合一定规律的。

处理方法:使用json模块,转化为python数据类型。

3.1数据提取之JSON

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转成字典

3.2案例-实现豆瓣电视剧爬虫

需求:爬取豆瓣电视的电视名、基本信息、封面。

实现步骤

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可以用来提取数据,保存文件)。

3.3案例-获取36kr网站首页的新闻

需求:爬取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): # >var props=(.*?)</script> result = re.findall('',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()

3.4案例-综合练习:有道翻译

需求:爬取有道翻译结果,学习解析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': '[email protected]; 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.
>
    > 小闫同学 </name> >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中不一定,比如换行标签

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:超文本标记语言,作用是渲染数据。

你可能感兴趣的:(爬虫学习(二))