当前位置:   article > 正文

Python爬虫(第四周)_mozilla/5.0 (windows nt 10.0; win64; x64) appleweb

mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like ge

目录

一、Session保持登录

二、代理IP的使用

三、timeout参数

四、retrying模块的使用

五、异步加载动态数据

六、爬虫中数据的分类以及解析

七、json格式数据

八、面向对象改写案例

九、json数据的解析 > jsonpath的使用


一、Session保持登录

 在上节我们讲到了人人网的案例,发送携带表单数据的post请求可以成功访问到登录后的页面,如果我们此时想通过此页面去访问别人的主页我们应该怎么做?

  1. import requests
  2. if __name__ == '__main__':
  3. # 1.确认目标的url > 登录的url
  4. url_ = 'http://www.renren.com/PLogin.do'
  5. # 用户代理
  6. headers_ = {
  7. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
  8. }
  9. # post请求携带数据,form表单..
  10. data_ = {
  11. 'email': 'xxxxxxxx', # 账号
  12. 'password': 'xxxxxxxx' # 密码
  13. }
  14. # 2.发送请求
  15. response_ = requests.post(url_, headers=headers_, data=data_)
  16. str_data = response_.text
  17. # 4.保存
  18. with open('renren02.html', 'w', encoding='utf-8') as f:
  19. f.write(str_data)
  20. # 找到详情页url_ > 需要登录之后才能够访问的
  21. url_ = 'http://www.renren.com/880151247/profile'
  22. response_ = requests.get(url_, headers=headers_)
  23. str_data = response_.text
  24. # 保存在本地
  25. with open('renren_dapeng.html', 'w', encoding='utf-8') as f:
  26. f.write(str_data)

通过上述代码我们可以成功访问到人人网登录后的个人主页,但是当访问别人主页的时候,又跳转到了登录页面。

出现这种情况的原因很简单,我们使用的python代码进行访问网站并不是浏览器,所以当我们去访问别人主页时并没有携带登陆后带有登录信息的cookie,这才导致我们访问失败。

搞清楚了原因后解决就变得轻松多了,我们只要在第二次发送请求时带上登陆后的cookie就可以成功了。但是这样的作法也存在着一些问题:

当我们要去访问100个人的主页时,我们要做100次的模拟登陆,然后再去携带100个不同的cookie,就很麻烦,此时我们引出一种更简单的做法。

利用requests的session方法,创建一个session类,利用session发送的请求就会自动携带登录过后保存有登录信息的cookie

  1. import requests
  2. if __name__ == '__main__':
  3. # 1.确认目标的url > 登录的url
  4. url_ = 'http://www.renren.com/PLogin.do'
  5. # 用户代理
  6. headers_ = {
  7. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
  8. }
  9. # post请求携带数据,form表单..
  10. data_ = {
  11. 'email': 'xxxxxxx', # 账号
  12. 'password': 'xxxxxxx' # 密码
  13. }
  14. # 2.如果想要发送post请求,模拟了登录之后,自动携带登陆过后的cookie,就要使用session方法
  15. session_ = requests.session() # 返回一个session对象
  16. session_.post(url_, headers=headers_, data=data_) # 之后session对象就保持了登录信息
  17. # 4.保存
  18. # with open('renren02.html','w',encoding='utf-8') as f:
  19. # f.write(str_data)
  20. # 找到详情页url_ > 需要登录之后才能够访问的
  21. url_ = 'http://www.renren.com/880151247/profile' # dapeng
  22. # 此时的session是保持了登录状态的,自动携带了带有登录信息的cookie
  23. response_ = session_.get(url_, headers=headers_)
  24. str_data = response_.text
  25. # 保存在本地
  26. with open('renren_dapeng02.html', 'w', encoding='utf-8') as f:
  27. f.write(str_data)

二、代理IP的使用

构造方法与请求头的构造方法一致,也是一个键值对

  1. proxy_ = {
  2. # 固定写法
  3. 'http': 'http://1.1.1.1:8888',
  4. 'https': 'https://1.1.1.1:8888'
  5. }

键名为http或者https,值为 http://IP地址:端口号 或 https://IP地址:端口号

  1. proxy_ = {
  2. # 固定写法
  3. 'http': '1.1.1.1:8888',
  4. 'https': '1.1.1.1:8888'
  5. }

第二种较为简单并且常用,键名与第一种方法一样,值为 IP地址:端口号

  1. import requests
  2. if __name__ == '__main__':
  3. url_ = 'https://www.baidu.com/'
  4. headers_ = {
  5. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
  6. }
  7. # 代理IP的构建,以键值对的方式
  8. # proxy_ = { # 固定的语法 ip地址:端口号
  9. # 'http':'1.1.1.1:8888'
  10. # }
  11. proxy_ = { # 固定的语法 ip地址:端口号
  12. 'http': 'http://1.1.1.1:8888' # 是一个无效的代理IP
  13. }
  14. response_ = requests.get(url_, headers=headers_, proxies=proxy_)
  15. bytes_data = response_.content
  16. # 保存一下
  17. with open('baidu01.html', 'wb') as f:
  18. f.write(bytes_data)

在访问网站时的用法如上

上述代码中的代理IP是我们瞎编的,为什么还有访问成功呢?因为requests最近更新后,当代理IP使用不成功时,会自动使用本机IP

测试代理IP的有效性:

测试网站:http://2021.ip138.com/

我们既然有了测试IP地址的网站,那么使用代理IP访问该网站就可以测试代理IP是否有效

  1. import requests
  2. from lxml import etree # 提取html格式数据的
  3. if __name__ == '__main__':
  4. # 特殊的测试IP的url
  5. url_ = 'http://2021.ip138.com/'
  6. headers_ = {
  7. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
  8. }
  9. # 代理IP的构建,以键值对的方式
  10. # proxy_ = { # 固定的语法 ip地址:端口号
  11. # 'http':'1.1.1.1:8888'
  12. # }
  13. proxy_ = { # 固定的语法 ip地址:端口号
  14. 'http': 'http://1.1.1.1:8888' # 是一个无效的代理IP
  15. }
  16. response_ = requests.get(url_, headers=headers_, proxies=proxy_)
  17. # response_ = requests.get(url_,headers=headers_)
  18. # print(response_)
  19. str_data = response_.text # 得到字符串类型的响应文本数据
  20. # 提取数据(后面会讲)
  21. html_obj = etree.HTML(str_data)
  22. res_ = html_obj.xpath('//title/text()')[0]
  23. print(res_)

如果程序报错,就证明代理IP无效

当我们使用代理IP池时,我们可以通过循环测试他们的有效性,将有效的代理IP加入到一个空列表中,当循环结束时,这个列表中的代理IP就全是有效的了。

三、timeout参数

当我们访问网站的时候,会出现响应时间过长的情况。

例如:我们在测试代理IP有效性的时候,即使有些代理IP有效,但存在着访问网站获取响应速度过慢的情况,所以我们需要把这样的IP也剔除掉

response_ = requests.get(url_, headers=headers_, proxies=proxy_, timeout=3)

我们在发送请求时加上timeout参数就可以了,例子中 timeout=3 ,就是在3秒内没有获得响应的话就立即报错

四、retrying模块的使用

见名知意,retrying重新尝试,即访问失败时重新发送请求

当我们要发送很多请求时,可能由于网络波动或者其他原因导致极少数的url请求不成功,那么我们是直接放弃这些url的请求吗?答案当然是否定的,我们可以使用retrying模块来实现多给他几次发送请求的机会

retrying模块是个第三方模块,需要自己下载。

我们使用的是retrying模块中的retry,通过查看retry,可以发现他是一个三层嵌套的闭包,用来做带有参数的装饰器,因为装饰器只能对函数和方法进行装饰,所以此处我们需要采用面向对象的编程方法

  1. import requests
  2. from retrying import retry # 怎么利用retry去创造多几次请求的机会
  3. class Demo:
  4. def __init__(self):
  5. self.url_ = 'xxxx.com'
  6. # 设置一个记数的变量
  7. self.num_ = 0
  8. # 发送请求的发送
  9. @retry(stop_max_attempt_number=3)
  10. def send_request(self):
  11. self.num_ += 1
  12. print(self.num_)
  13. requests.get(self.url_) # 报错,导致下面的代码执行不到
  14. def run(self):
  15. try:
  16. self.send_request()
  17. except Exception as e:
  18. print(e)
  19. if __name__ == '__main__':
  20. demo = Demo()
  21. demo.run()

因为我们要请求的网站是瞎编的,所以请求不会成功,以此我们来测试一下retry功能,retry中有一个参数:stop_max_attempt_number ,最多尝试次数,即我们请求失败时,有多少次重新请求的机会。

五、异步加载动态数据

异步:多任务可以同时进行,而且互不干扰

加载:页面的渲染

动态:页面没有发生跳转的情况下,里面的数据发生了变化

例子:https://movie.douban.com/chart页面右边电影的分类随便选择一类

当我们向下滑动页面时,页面并没有发生变化,但会有新的电影信息加载出来,这就是典型的异步加载的动态数据

分析:

异步加载:既然有新的数据产生,所以必定发送了相应的请求,在例子中就是向下滑动页面时触发了js代码中的ajax,然后发送了请求,获取了响应

动态数据:这些响应中就有数据,而这些数据我们就称之为动态数据

异步加载中说到发送了相应的请求,那必然是对不同的url进行了访问,html格式的一般都是静态数据,因为他是页面架构;动态数据是之后去填充页面的数据,基本上都是json格式。我们想要电影的信息,他是随着我们不断的向下滑动鼠标才能加载出来,即这些数据为动态数据。我们可以在鼠标右键检查的network中的xhr找到对应的动态数据包(动态数据包一般都可以在xhr中找到),我们在豆瓣电影的例子中发现一个动态数据包中有20条数据(20部电影的信息......),当鼠标向下滑动触发了ajax,就有请求发送,新的数据包就随之产生,然后新的数据包对页面进行填充。当向下滑动后,产生新的数据包,就意味着进行了翻页操作,1个url对应20条数据,当我们需要100条数据时,就得找到5个这样的url,而数据包的产生是类似的,即翻页是类似的,那么他们的url是否会存在一定的规律呢?

我们以豆瓣电影的爱情类电影为例:

  1. '''
  2. 第一页:https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&start=0&limit=20
  3. 第二页:https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&start=20&limit=20
  4. 第三页:https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&start=40&limit=20
  5. 第四页:https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&start=60&limit=20
  6. 第五页:https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&start=80&limit=20
  7. '''

我们通过对比分析,发现只有start的值不同,而且每一页之间的差为20,这就是翻页的规律

  1. import requests
  2. from fake_useragent import FakeUserAgent
  3. import time
  4. if __name__ == '__main__':
  5. # 确认要抓取数据的页面个数
  6. pages_ = int(input('请输入要抓取数据的页面个数:'))
  7. for page_ in range(pages_):
  8. # 1.确认目标的url
  9. url_ = f'https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&start={20 * page_}&limit=20'
  10. # 构造请求头
  11. headers_ = {
  12. 'User-Agent': FakeUserAgent().random
  13. }
  14. # 2.发送请求
  15. response_ = requests.get(url_, headers=headers_)
  16. content_ = response_.content
  17. # 3.解析数据(略)
  18. # 4.保存数据
  19. with open(f'豆瓣{page_ + 1}.json', 'wb') as f:
  20. f.write(content_)
  21. # 降低请求频率,防止被反爬
  22. time.sleep(1)

我们现在需要五个页面的数据,即需要对页面发送请求,而且他们的url存在规律,每个url中start的值相差20,所以我们可以用一个循环。一到五页的start值为:0,20,40,60,80,range(pages_)的值分别为0,1,2,3,4,所以代码中的start值应该为page_ * 20

六、爬虫中数据的分类以及解析

Python中一般都是操作字符串类型的数据

json格式数据的提取,解析:需要一个第三方库:jsonpath,re作为辅助

html格式数据的提取,解析:xpath,re作为辅助

七、json格式数据

爬虫部分:我们从前端拿到(爬到)的json格式的数据,应该怎么转换成为python能够操作的数据呢?

后端部分:我们python提供的数据,怎么转换成为json格式的数据供前端使用呢?

python 转 json 用json中的dumps方法

  1. import json
  2. if __name__ == '__main__':
  3. # python中的字典
  4. python_data = {
  5. 'name': '小明',
  6. 'age': '18',
  7. }
  8. print(type(python_data), python_data)
  9. # <class 'dict'> {'name': '小明', 'age': '18'}
  10. # python转json 用dumps函数,如果需要显示中文,需要添加参数
  11. json_data = json.dumps(python_data)
  12. print(type(json_data), json_data)
  13. # <class 'str'> {"name": "\u5c0f\u660e", "age": "18"}
  14. json_data = json.dumps(python_data, ensure_ascii=False)
  15. print(type(json_data), json_data)
  16. # <class 'str'> {"name": "小明", "age": "18"}
  17. # 如果需要格式更加美观,需要添加一个indent参数
  18. json_data = json.dumps(python_data, ensure_ascii=False, indent=3)
  19. print(json_data)
  20. '''
  21. {
  22. "name": "小明",
  23. "age": "18"
  24. }
  25. '''

需要注意的是:

1.我们定义python中字典是用的都是单引号,在转换成json格式后,所有单引号变成了双引号,这是因为json中的引号必须是双引号

2.我们定义python中字典时在最后面加了个逗号,在转换成json格式后,末尾的逗号消失了,这是因为json格式中末尾不能有逗号

3.python转json格式时如果不加参数:ensure_ascii=False,那么python格式中的中文在转化成json格式后会出现乱码

json 转 python 用json中的loads方法

  1. import json
  2. if __name__ == '__main__':
  3. # python中的字典
  4. python_data = {
  5. 'name': '小明',
  6. 'age': '18'
  7. }
  8. print(type(python_data), python_data)
  9. # <class 'dict'> {'name': '小明', 'age': '18'}
  10. json_data = json.dumps(python_data, ensure_ascii=False, indent=3)
  11. print(type(json_data), json_data)
  12. '''
  13. <class 'str'>
  14. {
  15. "name": "小明",
  16. "age": "18"
  17. }
  18. '''
  19. # json 转 python 用loads函数
  20. py_data = json.loads(json_data)
  21. print(type(py_data), py_data)
  22. # <class 'dict'> {'name': '小明', 'age': '18'}

如何将python字典写入json文件?

  1. import json
  2. if __name__ == '__main__':
  3. file = open('json01.json', 'w')
  4. # 创建一个python字典
  5. python_dict = {
  6. 'name': '小明',
  7. 'age': 18
  8. }
  9. # 将python字典写入json文件用dump方法
  10. # 正常显示中文需要添加参数ensure_ascii=False,格式好看需要加入一个参数indent
  11. json.dump(python_dict, file, ensure_ascii=False, indent=3)

如何将json文件读入python转化为字典格式?

  1. import json
  2. if __name__ == '__main__':
  3. file = open('json01.json', 'r')
  4. # 将json文件读入python转化为字典格式用load
  5. py_data = json.load(file)
  6. print(py_data) # {'name': '小明', 'age': 18}

总结:

loads:json字符串        >        python

load:json文件        >        python

dumps:python        >        json字符串

dump:python        >        json文件

八、面向对象改写案例

非常繁琐,不是很推荐

  1. import requests
  2. import time
  3. class DouBan:
  4. def __init__(self):
  5. """定义静态的属性,url,User-Agent"""
  6. self.url_ = 'https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&limit=20'
  7. self.headers_ = {
  8. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
  9. }
  10. def send_request(self, params_): # url传参
  11. """发送请求,获取响应"""
  12. response_ = requests.get(self.url_, headers=self.headers_, params=params_)
  13. str_data = response_.text
  14. return str_data
  15. def save_file(self, page, str_data):
  16. """保存文件"""
  17. with open(f'{page + 1}.json', 'w', encoding='utf-8') as f:
  18. f.write(str_data)
  19. def run(self):
  20. """调度方法"""
  21. pages = int(input('请输入想要抓取的页数:'))
  22. for page in range(pages):
  23. # 定义url的start参数
  24. params_ = {
  25. 'start': page * 20
  26. }
  27. # 调用发送请求的方法
  28. data_ = self.send_request(params_)
  29. # 调用保存的方法
  30. self.save_file(page, data_)
  31. # 降低请求频率,避免被反爬
  32. time.sleep(1)
  33. if __name__ == '__main__':
  34. douban_ = DouBan()
  35. douban_.run()

九、json数据的解析 > jsonpath的使用

网站:json.cn        把json格式的数据复制进去,可以使结构更美观,清晰

使用方法举例:

  1. import jsonpath
  2. data_ = { "store": {
  3. "book": [
  4. { "category": "reference",
  5. "author": "Nigel Rees",
  6. "title": "Sayings of the Century",
  7. "price": 8.95
  8. },
  9. { "category": "fiction",
  10. "author": "Evelyn Waugh",
  11. "title": "Sword of Honour",
  12. "price": 12.99
  13. },
  14. { "category": "fiction",
  15. "author": "Herman Melville",
  16. "title": "Moby Dick",
  17. "isbn": "0-553-21311-3",
  18. "price": 8.99
  19. },
  20. { "category": "fiction",
  21. "author": "J. R. R. Tolkien",
  22. "title": "The Lord of the Rings",
  23. "isbn": "0-395-19395-8",
  24. "price": 22.99
  25. }
  26. ],
  27. "bicycle": {
  28. "color": "red",
  29. "price": 19.95
  30. }
  31. }
  32. }
  33. # 使用方法
  34. res_ = jsonpath.jsonpath(data_,'$.store.book[*].author')
JSON路径结果
$.store.book[*].author商店中所有书籍的作者
$..author所有的作者
$.store.*商店下所有的元素
$.store..price商店中所有内容的价格
$..book[2]第三本书
$..book[(@.length-1)] | $..book[-1:]一本书
$..book[0,1] | $..book[:2]前两本书
$..book[?(@.isbn)]获取有isbn的所有数
$..book[?(@.price<10)]获取价格大于10的所有的书
$..*获取所有数据

 使用方法:jsonpath.jsonpath(对象,jsonpath路径)

使用jsonpath提取到的数据是一个列表

jsonpath的语法并不固定,要取同一个内容,jsonpath路径可能不同

注意:

简单方法中是使用了response的json方法,不是导入的json模块

  1. # 将得到的json格式的数据转换为python能够操作的格式
  2. data_ = response_.text
  3. py_data = json.loads(data_)
  4. # 简单方法
  5. py_data = response_.json()

实际使用(豆瓣电影):

  1. import jsonpath
  2. import requests
  3. import json
  4. if __name__ == '__main__':
  5. # 1.确认目标url
  6. url_ = 'https://movie.douban.com/j/chart/top_list?type=13&interval_id=100%3A90&action=&start=0&limit=20'
  7. # 构造用户代理
  8. headers_ = {
  9. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
  10. }
  11. # 2.发送请求,获取响应
  12. response_ = requests.get(url_, headers=headers_)
  13. # 转换为python格式
  14. py_data = response_.json()
  15. # 3.解析 title score
  16. title_list = jsonpath.jsonpath(py_data, '$..title') # 列表
  17. score_list = jsonpath.jsonpath(py_data, '$..score')
  18. print(title_list)
  19. print(score_list)
  20. # 4.保存
  21. for i in range(len(title_list)): # 一条一条数据写入,,让格式看起来更舒服
  22. dict_ = {}
  23. dict_[title_list[i]] = score_list[i]
  24. # 转成json字符串写入
  25. json_data = json.dumps(dict_,ensure_ascii=False) + ',\n' # 显示中文 换行
  26. with open('duoban_03.json', 'a', encoding='utf-8') as f: # 进行追加不覆盖
  27. f.write(json_data)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/101736
推荐阅读
相关标签
  

闽ICP备14008679号