前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >爬虫学习(二)

爬虫学习(二)

作者头像
小闫同学啊
发布2019-07-18 15:11:12
7840
发布2019-07-18 15:11:12
举报
文章被收录于专栏:小闫笔记小闫笔记

正文共: 10409字 6图 预计阅读时间: 27分钟

每日分享

To understand and be understood, those are among life’s greatest gifts, and every interaction is an opportunity to exchange them.

理解与被理解,都是生活中最棒的礼物。每次的互动都是交换它们的一次机会。

小闫语录

被误会,被责备,被嘲笑,在那一刻,你是多么的渴望被理解,犹如久旱逢甘霖一般。只有经历过的人才会明白被理解是多么的美好珍贵。其实还有一样东西与此同样重要,那便是理解。你渴望的同样也是其他人渴望的,去理解他人,换回的将是被理解。当与其他人发生矛盾的时候,尝试站到对方的角度去思考一下,许多矛盾都会迎刃而解。

爬虫学习(二)

bug

环境:Windows10企业版,版本号1809;pycharm2017.3.3。

背景:在将爬取的数据执行写入文件操作时报错。

代码

代码语言:javascript
复制
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。

解决办法

代码语言:javascript
复制
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。

代码语言:javascript
复制
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表示账号密码。

代码语言:javascript
复制
proxy2 = {
    'http':'http://user:password@103.230.35.222:3128',
    'https':'https://user:password@103.230.35.222: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。

代码语言:javascript
复制
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的本质是键值对形式的字符串。

代码语言:javascript
复制
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.获取响应,解析响应数据。

代码语言:javascript
复制
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:

代码语言:javascript
复制
reqeusts.utils.dict_from_cookiejar

把字典格式的cookies转换成 cookiejar对象:

代码语言:javascript
复制
requests.utils.cookiejar_from_dict

2.请求SSL证书验证

如果访问一个网站,遇到SSL错误信息,原因是该网站的CA认证证书不是标准的。

使用场景:Requests 可以为 HTTPS 请求验证 SSL 证书,就像 web 浏览器一样。SSL 验证默认是开启的,如果证书验证失败,Requests 会抛出 SSLError

使用方式

代码语言:javascript
复制
response = requests.get("https://www.12306.cn/mormhweb/ ", verify=False)

3.设置超时

使用场景:有些站点或者代理反应慢,严重降低效率,这个时候可以设置超时。

使用方式

代码语言:javascript
复制
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并不是一种数据类型。

代码语言:javascript
复制
json.dumps # 把字典转为json---操作的是变量
json.loads # 把json转成字典
json.dump # 把字典转为json---操作的是文件对象(具有read和write方法的对象)
json.load # 把json转成字典

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

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

实现步骤

1.构建请求信息。

2.发送请求,获取响应。

3.解析响应数据

4.保存数据。

技术点:使用json模块,结构化数据。

代码语言:javascript
复制
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模块。

代码语言:javascript
复制
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()

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

需求:爬取有道翻译结果,学习解析js代码。

是结构化数据

在浏览器中找到对应的js文件

1.ctrl + F,查询关键字(一定是特殊的,不要找普通的,都具有的那种关键字)。

2.network中,找到对应的数据包,在后面找到initiator列中的js文件点进去。

3.根据标签对应的事件监听。

步骤

1.构建请求信息。

2.发送请求,获取响应。

3.解析响应数据。

4.运行。

重点

1.js解析的过程,根据需要查找的关键字进行js的解析。

2.有道翻译的反爬措施:请求头重必须要有referer和cookie,缺一不可。

代码语言:javascript
复制
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格式传输。

代码语言:javascript
复制
xmltodict模块:把xml数据转成字典,unparse/parse.
代码语言:javascript
复制
<xml>
    <name> 小闫同学 </name>
    <age>18</age>
</xml>

5.json格式数据:键值对形式的字符串;作用是传输数据。

代码语言:javascript
复制
{"name":"Ethanyan","age":18}

6.json:解决了不同平台、不同语言之间的数据交互。

代码语言:javascript
复制
<!--前端:对象-->
var parmas = {'name':'Ethanyan'....}
<!--把对象转成json-->
JSON.stringify(params) 

<!--后端:字典-->
parmas = {'name':'Ethanyan'....}
<!--把字典转成json-->
json.dumps(params)

7.xml都是闭合标签;html中不一定,比如换行标签 <br>

8.正则表达式匹配ip地址:

代码语言:javascript
复制
# 最终版
(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:超文本标记语言,作用是渲染数据。

优质文章推荐:

公众号使用指南

redis操作命令总结

前端中那些让你头疼的英文单词

Flask框架重点知识总结回顾

项目重点知识点详解

难点理解&面试题问答

flask框架中的一些常见问题

团队开发注意事项

浅谈密码加密

Django框架中的英文单词

Django中数据库的相关操作

DRF框架中的英文单词

重点内容回顾-DRF

Django相关知识点回顾

美多商城项目导航帖

项目重要技术点介绍

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈技术精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 每日分享
  • 爬虫学习(二)
    • bug
      • 1.使用IP代理
        • 1.1使用代理
      • 2.cookie和session
        • 2.1二者区别
        • 2.2利弊与抉择
        • 2.3案例-使用cookie来获取登录之后人人网的响应
        • 2.4案例-使用session来登录人人网
        • 2.5requests小技巧
      • 3.数据提取
        • 3.1数据提取之JSON
        • 3.2案例-实现豆瓣电视剧爬虫
        • 3.3案例-获取36kr网站首页的新闻
        • 3.4案例-综合练习:有道翻译
      • 小知识点
      相关产品与服务
      数据库
      云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档