文章目录
数据提取概念
-
什么是数据提取?
简单的来说,数据提取就是从响应中获取我们想要的数据的过程
数据分类
-
非结构化数据, 如html
-
处理方法: 正则表达式/xpath
html数据
-
结构化数据, 如json/xml
- 处理方法: 使用json或xpath转换为Python对应的数据类型
-
JSON
-
XML
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
JSON数据提取
-
JSON的概念
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。 -
JSON与前后台交互
JSON在数据交换中起到了一个载体的作用,承载着相互传递的数据
json模块
- json模块概述:
json模块是Python自带的模块, 用于json与python数据之间的相互转换. - 使用json模块处理JSON数据
- 什么是包含json的类文件对象?
-
就是一个使用open打开的指向json格式文件的对象 rf = open("xx.json", 'r') 这里rf就是 wf = open("xx.json", "w") 这里wf也是
- dump与dumps的两个参数
- ensure_ascii: 是否使用ascii方式写入,默认True, 中文就会显示编码的后内容,如果指定False则以UTF-8编码写入,此时打开文件也必须指定UTF-8
- indent: 用于指定代码块之间缩进几个空格
测试代码
json模块使用
import json
# 准备json格式的字符串
json_str = '{"a":10, "b":20, "c":true, "d":["a","b","中文"]}'
# 把json字符串转为为python
dic = json.loads(json_str)
print(dic)
# 把python中数据类型转换为json字符串
json_s = json.dumps(dic)
print(json_s)
# 操作文件
# with open("test.json", 'w', encoding='utf8') as f:
# json.dump(dic, f)
with open("test.json", 'w', encoding='utf8') as f:
json.dump(dic, f, ensure_ascii=False, indent=2)
实例:获取豆瓣热映电影信息
需求: 获取获取热映电影信息, 保存到文件中
实现爬虫四部曲:
准备URL
发送请求,获取响应数据
解析响应数据
保存解析后的数据
# 1. 准备URL
# - 准备一个获取豆瓣热映电影信息的json数据的URL
# - 如果电脑web版没有找到json数据, 可以尝试使用手机版
# https://m.douban.com/rexxar/api/v2/subject_collection/movie_showing/items?start=0&count=18&loc_id=108288
# 豆瓣电影json数据对用的URL
douban_movie_url = "https://m.douban.com/rexxar/api/v2/subject_collection/movie_showing/items?start=0&count=18&loc_id=108288"
# 2. 根据这个URL,发送请求获取热映电影json数据
# 2.1 直接发送请求,获取的数据是错误的,但是我们浏览器确实可以获取正确数据
# 2.2 我们在发送请求的时候,模拟浏览器,要模拟更像一点. 增加请求头相关信息
# 定义请求头
headers = {
"Referer": "https://m.douban.com/movie/nowintheater?loc_id=108288",
"User-Agent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Mobile Safari/537.36"
}
response = requests.get(douban_movie_url, headers=headers)
# 通过response获取我们要的json数据
json_str = response.content.decode()
# print(json_str)
# 3. 解析数据(json) -> 把json字符串 -> python数据类型
movie_items = json.loads(json_str)
# print(movie_items)
# 4. 保存数据
with open("douban_movie.json", 'w', encoding='utf8') as f:
json.dump(movie_items, f, ensure_ascii=False, indent=4)
使用来封装上面的代码
也就是把代码封装到类中, 每一个功能通过方法实现
class DoubanMovieSpider(object):
def __init__(self):
''' 初始化方法 '''
# 模板URL
self.url_pattern = "https://m.douban.com/rexxar/api/v2/subject_collection/movie_showing/items?start={}&count=18&loc_id=108288"
# 请求头
self.headers = {
"Referer": "https://m.douban.com/movie/nowintheater?loc_id=108288",
"User-Agent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Mobile Safari/537.36"
}
def get_json_from_url(self, url):
'''
# 2. 根据这个URL,发送请求获取热映电影json数据
:param url: 请求的URL
:return: json格式的字符串
'''
response = requests.get(url, headers=self.headers)
return response.content.decode()
def get_movie_list(self, json_str):
'''
3. 解析数据(json), 返回电影的列表信息
:param json_str: json字符串
:return: 电影的列表信息
'''
dic = json.loads(json_str)
# 通过字典获取电影的列表数据
movie_list = dic['subject_collection_items']
return movie_list
def save_movie_list(self, movie_list):
'''
# 4. 保存数据, 每一个电影信息保存到一行上
:param movie_list: 电影列表信息
'''
# 为了提高写数据的效率,先打开文件在遍历. 每一次打开文件都比较消耗性能的.
with open("movies.txt", 'a', encoding='utf8') as f:
for movie in movie_list:
json.dump(movie, f, ensure_ascii=False)
f.write("\n")
def run(self):
'''
这是这个爬虫的入口方法
'''
# 定义一个变量,用于记录起始索引号
url = self.url_pattern.format(0)
# 2. 根据这个URL,发送请求获取热映电影json数据
json_str = self.get_json_from_url(url)
# 3. 解析数据(json), 获取电影列表信息
movie_list = self.get_movie_list(json_str)
# 4. 保存数据, 每一个电影信息保存到一行上
self.save_movie_list(movie_list)
实现分页效果
- 要实现分页,必须找到找到下一页的URL,由于下一页与这一页数据格式是一样的, 所以可以使用循环来实现, 并且在没有数据的时候退出
- 豆瓣电影的实现方式有两种:
方式1: 根据URL规律找生成下一页URL,当返回的数据条数小于请求的数据条数的时候退出
变化的代码
def run(self):
'''
这是这个爬虫的入口
'''
# 定义一个变量,用于记录起始索引号
start = 0
while True:
# 1. 准备URL
url = self.url_pattern.format(start)
# 2. 根据这个URL,发送请求获取热映电影json数据
json_str = self.get_json_from_url(url)
# 3. 解析数据(json), 获取电影列表信息
movie_list = self.get_movie_list(json_str)
# 4. 保存数据, 每一个电影信息保存到一行上
self.save_movie_list(movie_list)
start += 18
# 如果这次请求返回的数据不足18条,就可以结束
if len(movie_list) < 18:
break
方式2:
'根据返回的数据计算出下一页的起始索引号, 如果起始索引号比总条数还大就说明没有数据了, 结束循环:
变化的代码
def get_movie_list(self, json_str):
'''
3. 解析数据(json), 返回电影的列表信息
:param json_str: json字符串
:return: 电影的列表信息
'''
dic = json.loads(json_str)
# 计算下一页起始号
# 本次获取的电影的条数
count = dic['count']
# 本次获取的请求是起始号是几
start = dic['start']
# 计算下一页的起始号
next_start= start + count
# 如果下一页开始的页码数大于登录总条数, 就没有下一页了
if next_start >= dic['total']:
next_start = None
# 通过字典获取电影的列表数据
movie_list = dic['subject_collection_items']
return movie_list, next_start
def run(self):
'''
这是这个爬虫的入口方法
方式2: 根据这一次请求的结果,计算下一页起始页号, 如果技术处理的起始页号比总数还大, 就结束
'''
# 定义一个变量,用于记录起始索引号
start = 0
while True:
# 1. 准备URL
url = self.url_pattern.format(start)
# 2. 根据这个URL,发送请求获取热映电影json数据
json_str = self.get_json_from_url(url)
# 3. 解析数据(json), 获取电影列表信息
movie_list, start = self.get_movie_list(json_str)
# 4. 保存数据, 每一个电影信息保存到一行上
self.save_movie_list(movie_list)
# start为None就结束循环
if start is None:
break