Python爬虫中Json数据的提取解析处理

Python爬虫中Json数据的提取解析处理

在爬取一些网页时,碰到Json格式的数据是很常见的,比如我们很熟悉的有道翻译就是json格式的数据。

在使用requests库进行请求时,我们可以直接使用json()方法,将字符串格式的json数据转化为字典格式,然后利用字典的键-值索引和列表索引配合使用解析json数据,或者使用get()方法和列表索引解析。

使用urllib库进行请求时,可以使用json.loads(...)方法,操作方法同上。

把Json格式字符串转化为python字典类型很简单,爬虫中如果我们能够找到返回json数据格式字符串的url,就会尽量使用这种url。

那如何找到返回Json的url呢?

  • 使用浏览器/抓包工具进行分析 wireshark(windows/linux),tcpdump(linux)
  • 抓包手机app的软件

关于提到的loads方法和爬虫时python使用json报错问题可以滑到后文看看,这篇建议新手先掌握一定的Python基础再接着往下看,不然看起来可能会有点云里雾里的。

有新手说基础看不进去,就是心太浮躁了,可以试试番茄时钟,适当集中,适当放松,学习方法可以参考一下这篇答主的文章,我这里就不多述了,我们重点讲Json。

传送门: Python学习方法参考

现在先回过头来看看Json的相关内容点:

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。

JSON基于两种结构

json简单说就是javascript中的对象和数组,所以这两种结构就是对象和数组,通过这两种结构可以表示各种复杂的结构。

① 对象

对象在js中表示为“{}”括起来的内容,数据结构为 {key:value,key:value,…}的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值,所以很容易理解,取值方法为 对象.key 获取属性值,这个属性值的类型可以是数字、字符串、数组、对象几种。

② 数组

数组在js中是中括号“[]”括起来的内容,数据结构为 [“java”,“javascript”,“vb”,…],取值方式和所有语言中一样,使用索引获取,字段值的类型可以是数字、字符串、数组、对象几种。

json有四个方法供我们进行数据转换

mydict = {'name': 'xiaoming', 'age': 18}
#json.dumps 实现python类型转化为json字符串
json_str = json.dumps(mydict)   
#json.loads 实现json字符串转化为python的数据类型
my_dict = json.loads(json_str)
#json.dump 实现把python类型写入类文件对象
with open("temp.txt","w") as f:
    json.dump(mydict,f,ensure_ascii=False,indent=2)
# json.load 实现类文件对象中的json字符串转化为python类型
with open("temp.txt","r") as f:
    my_dict = json.load(f)

JSON支持数据格式:

  • 对象(字典)使用花括号
  • 数组(列表)使用方括号
  • 整形、浮点型、布尔类型还有null类型
  • 字符串类型(字符串必须要用双引号,不能用单引号)

多个数据之间使用逗号分开;

Json在数据交换中起到了一个载体的作用,承载相互传递的数据。

Python3 中使用 json 模块来对 JSON 数据进行编解码,主要包含了下面4个操作函数:

注意:使用 JSON 函数需要导入 json 库:import json。

类文件对象的理解:

类文件对象指那些具有read()或者 write()方法的对象,例如,f = open('a.txt','r'),其中的f有read()方法,所以f就是类文件对象。

在json的编解码过程中,python 的原始类型与JSON类型会相互转换,具体的转化对照如下:

Python 编码为 JSON 类型转换对应表

JSON 解码为 Python 类型转换对应表

注意:使用eval()能够实现简单的字符串和Python类型的转化

示例1:python字典和列表转JSON

import json
    books = [
            'title': '钢铁是怎样练成的',
            'price': 9.8
            'title': '红楼梦',
            'price': 9.9
    json_str = json.dumps(books,ensure_ascii=False)
    print(json_str)

因为json在dump的时候,只能存放ascii的字符,因此会将中文进行转义,这时候我们可以使用ensure_ascii=False关闭这个特性。

在Python中只有基本数据类型才能转换成JSON格式的字符串,即:int、float、str、list、dict、tuple;

提醒一点:Python的数据结构应用在爬虫中是高频知识点,学习的时候要避免只会知识,不会用的情况。

Python学习资源的推荐可以看看下面这篇文章,对于入门来说是没问题的。

zhihu.com/question/4516

示例2:将json数据直接dump到文件中

    books = [
            'title': '钢铁是怎样练成的',
            'price': 9.8
            'title': '红楼梦',
            'price': 9.9
    with open('a.json','w') as fp:
        json.dump(books,fp)

json模块中除了dumps函数,还有一个dump函数,这个函数可以传入一个文件指针,直接将字符串dump到文件中。

具体使用方法示例:

#json.dumps 实现python类型转化为json字符串
#indent实现换行和空格
#ensure_ascii=False实现让中文写入的时候保持为中文
json_str = json.dumps(mydict,indent=2,ensure_ascii=False)
#json.loads 实现json字符串转化为python的数据类型
my_dict = json.loads(json_str)
#json.dump 实现把python类型写入类文件对象
with open("temp.txt","w") as f:
    json.dump(mydict,f,ensure_ascii=False,indent=2)
# json.load 实现类文件对象中的json字符串转化为python类型
with open("temp.txt","r") as f:
    my_dict = json.load(f)

loads方法

loads(字符串, encoding = ‘utf-8’) - 将字符串中的json数据转换成对应的python数据

import json
content = json.loads('"abc"', encoding = 'utf-8')
print(content, type(content)) # abc <class 'str'>
注意:字符串中的内容必须是字符串数据;

示例3:将一个json字符串load成Python对象

    json_str = '[{"title": "钢铁是怎样练成的", "price": 9.8}, {"title": "红楼梦", "price": 9.9}]'
    books = json.loads(json_str,encoding='utf-8')
    print(type(books))
    print(books)

json文件处理

示例4:直接从文件中读取json

    import json
    with open('a.json','r',encoding='utf-8') as fp:
        json_str = json.load(fp)
        print(json_str)
提示:json文件不一定是后缀是.json都是json文件,json文件是文件内容是json数据的文件。

load(文件对象) - 将指定文件中的内容读出来,并且转换成python对应数据,文件对象对应的文件必须是json文件。

dump(对象, 文件对象) - 将指定对象转换成是json格式的字符串,然后写入指定的文件中。

注意:这儿的对象对应的类型必须是能够转换成json的数据类型

Json数据来源

python中的数据请求(HTTP请求),是通过第三方库requsets来提供的。

requests的第三方库的使用

get/post 方法都是发送请求获取接口提供的数据,获取数据的方法都是get请求

get(url, params = None)

url - 需要获取的数据的接口地址

params - 字典,参数列表(给服务器发送请求的时候需要传给服务器的数据)

完整的接口:协议://主机地址/路径?参数名1=值1&参数名2=值2

发送请求,并且获取返回的数据,服务器返回的数据叫响应

从响应中获取数据

1)获取json数据

content_json = response.json() # 会自动将json数据转换python对应的数据

2)获取字符串数据

content_text = response.text
print(type(content_text))
print(content_text)

3)获取二进制数据

content_bytes = response.content # 获取图片,音频等
print(content_bytes)

post(url, params = None, json = None)

python使用json时报错问题

1)str转换成json时使用json.dumps出现乱码(原始字符编码)

>>> import json
>>> srt = js = json.loads('{"haha": "哈哈"}')
>>> print json.dumps(js)
{"name": "\u54c8\u54c8"}

解决办法:

>>> print json.dumps(js, ensure_ascii=False)   
{"name": "哈哈"}

2)把str转换为json后出现反斜杠转义字符

解决办法:直接把json中的反斜杠替换掉

json_data = json.dumps(str)
json_data = json_data.replace('\\', '')

3)UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

字符串在Python内部的表示是unicode编码,因此在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。

所以转码的时候一定要先搞明白,字符串str是什么编码,然后decode成unicode,然后再encode成其他编码,代码中字符串的默认编码与代码文件本身的编码一致。

解决办法:

# _*_ coding:utf-8 _*_
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )

案例示例:

1)爬取豆瓣电视剧上英剧和美剧两个分类的电视数据

# coding=utf-8
import requests
import json
class Douban:
    def __init__(self):
        self.url_temp_list = [
                "url_temp": "https://m.douban.com/rexxar/api/v2/subject_collection/filter_tv_american_hot/items?start={}&count=18&loc_id=108288",
                "referer": "https://m.douban.com/tv/american"
                "url_temp": "https://m.douban.com/rexxar/api/v2/subject_collection/filter_tv_english_hot/items?start={}&count=18&loc_id=108288",
                "referer": "https://m.douban.com/tv/british"
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Mobile Safari/537.36"
    # 获取响应的数据
    def parse_url(self, url):
        print(url)
        resp = requests.get(url, headers = self.headers)
        json_str = resp.content.decode()
        return json_str
    # 提取获取的数据
    def get_content_list(self,json_str):
        temp_dict = json.loads(json_str)
        return temp_dict["subject_collection_items"]
    # 保存搜索到的数据
    def save_content_list(self,content_list):
        with open("douban.txt","a",encoding="utf-8") as f:
            for content in content_list:
                f.write(json.dumps(content,ensure_ascii=False))
                f.write("\n")
        print("保存成功")
    def run(self):
        for url_temp in self.url_temp_list:
            self.headers.update({"referer":url_temp["referer"]})
            num = 0
            while True:
                # 获取url地址
                next_url = url_temp["url_temp"].format(num)
                # 发送请求,获取响应的数据
                json_str = self.parse_url(next_url)
                # 提取数据
                content_list = self.get_content_list(json_str)
                # 保存搜索到的数据
                self.save_content_list(content_list)
                num+=18
                if len(content_list)<18:
                    break
if __name__ == '__main__':
    douban = Douban()
    douban.run()

2)爬取果壳上的数据源

# coding=utf-8
import requests
import re
import json
class Gouke:
    def __init__(self):
        self.url_temp = "https://www.guokr.com/ask/highlight/?page={}"
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Mobile Safari/537.36"
    def get_url_list(self):
        return [self.url_temp.format(i) for i in range(1,21)]
    def parse_url(self, url):
        print(url)
        resp = requests.get(url, headers=self.headers)
        html_str=resp.content.decode()
        return html_str
    def get_content_list(self,html_str):
        content_list = re.findall(r"<h2><a target=\"_blank\" href=\"(.*?)\">(.*?)</a></h2>",html_str,re.S)
        return content_list
    def save_content_list(self,content_list): #提取数据,保存
        with open("guoke.txt", "a", encoding="utf-8") as f:
            for content in content_list:
                f.write(json.dumps(content, ensure_ascii=False))
                f.write("\n")
        print("保存成功")
    def run(self):#实现主要逻辑
        #1. url_list
        url_list = self.get_url_list()
        #2. 遍历,发送请求,获取响应
        for url in url_list:
            html_str = self.parse_url(url)
            #3. 提取数据
            content_list = self.get_content_list(html_str)