(三)爬虫之数据提取

数据提取

什么是数据提取? 简单的来说,数据提取就是从响应中获取我们想要的数据的过程


数据分类

  1. 非结构化数据: html , 文本等
    处理方法:正则表达式,xpath语法

2.结构化数据:json,xml等
处理方法:转换为python数据类型

复习JSON知识

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。

为什么要使用JSON?

把json格式字符串转化为python字典类型很简单,所以爬虫中,如果我们能够找到返回json数据格式字符串的url,就会尽量使用这种url 如何找到返回json的url呢?
1、使用浏览器/抓包工具进行分析 wireshark(windows/linux),tcpdump(linux)
2、抓包手机app的软件


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

  1. 对象:对象在js中表示为{ }括起来的内容,数据结构为 { key:value, key:value, ... }的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值,所以很容易理解,取值方法为 对象.key 获取属性值,这个属性值的类型可以是数字、字符串、数组、对象这几种
  2. 数组:数组在js中是中括号[ ]括起来的内容,数据结构为 ["Python", "javascript", "C++", ...],取值方式和所有语言中一样,使用索引获取,字段值的类型可以是 数字、字符串、数组、对象几种。

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


1. json.loads()

把Json格式字符串解码转换成Python对象

2.json.dumps()

实现python类型转化为json字符串,返回一个str对象 把一个Python对象编码转换成Json字符串


3.json.dump()

将Python内置类型序列化为json对象后写入文件

4. json.load()

读取文件中json形式的字符串元素 转化成python类型

import json

# python中支持单引号,但转换数据类型的时候,需要使用双引号
dict_data = '{"neuedu":"沈阳_python24", "pub_date": "2019-4-14 17:00:00"}'
print(dict_data)
print(type(dict_data))


# 转成json字符串 默认使用ascii编码,需要设置ensure_ascii为False
json_data = json.dumps(dict_data,ensure_ascii=False)
print(json_data)
print(type(json_data))

# 转成字典
dict_data_str = json.loads(dict_data)
print(dict_data_str)
print(type(dict_data_str))

# 把字典转成json写入文件,写文件如果遇到编码ascii错误,可以指定编码格式为utf8
f = open('data.json','w',encoding='utf8')
json.dump(dict_data_str,f,ensure_ascii=False)

# 读取json文件中的内容,转成字典
f = open('data.json','r',encoding='utf8')
dict_data = json.load(f)
print (dict_data)

案例 爬取豆瓣网电视信息


打开ajax请求页会展示如下代码


import requests
import json

"""
目标:
爬取豆瓣网电视信息
1、电视名称、封面信息、评分等
"""
class Douban(object):

    def __init__(self):
        self.url = 'https://movie.douban.com/j/search_subjects?type=tv&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=200&page_start=1'
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }
        # 创建文件对象
        self.file = open('douban.json','w',encoding='utf8')

    # 发送请求,获取数据
    def get_data(self):
        response = requests.get(url=self.url,headers=self.headers)
        # 把响应数据转成str类型
        return response.content.decode()

    # 解析数据
    def parse_data(self,data):
        # 把电视数据转成字典
        dict_data = json.loads(data)
        # print(dict_data)
        result_list = dict_data['subjects']
        data_list = []
        # 获取电视列表数据
        for result in result_list:
            temp = {}
            temp['title'] = result['title']
            temp['rate'] = result['rate']
            temp['url'] = result['url']
            data_list.append(temp)
        # 返回数据列表
        return data_list

    # 保存电视数据
    def save_data(self,data_list):
        # 遍历列表
        for data in data_list:
            str_data = json.dumps(data,ensure_ascii=False) + ',\n'
            self.file.write(str_data)

    # 关闭文件对象
    def __del__(self):
        self.file.close()

    def run(self):
        # 1、构建请求头和url
        # 2、发送请求,获取数据
        data = self.get_data()
        # 3、解析数据
        data_list = self.parse_data(data)
        # 4、保存文件
        self.save_data(data_list)


if __name__ == '__main__':
    douban = Douban()
    douban.run()

工作空间生成douban,.json文件


正则表达式

就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑



使用正则进行猫眼经典影片电影页面信息爬取

地址: https://maoyan.com/board/4?offset=0

import requests
import re
import json
from multiprocessing import Pool
from requests.exceptions import RequestException\

def get_one_page(url):
    try:
        headers={'User-Agent':'Mozilla/5.0'}
        r=requests.get(url,headers=headers)
        r.encoding=r.apparent_encoding
        r.raise_for_status()
        #print(r.text)
        return r.text

    except requests.exceptions.RequestException:
        print('requests.exceptions.RequestException')
        return None


def parse_one_page(html):
    pattern=re.compile('<dd>.*?<i class="board-index.*?">(\d+)</i>'
                       +'.*?title="(.*?)".*?'
                       +'<img data-src="(.*?)".*?'
                       + 'class="star">(.*?)</p>.*?'
                       + 'releasetime">(.*?)</p>'
                       + '.*?integer">(\d+.)</i>'
                       + '.*?fraction">(\d).*?</dd>'
                       ,re.S)
    items=pattern.findall(html)

    for item in items:
        yield {
            'sequence':item[0],
            'title':item[1],
            'picture':item[2],
            'actors':item[3].strip()[3:],
            'date':item[4][5:],
            'score':item[5]+item[6]}


def write_to_file(content):
    with open('movie.txt','a',encoding='utf-8') as f:
        jsondumps=json.dumps(content,ensure_ascii=False)
        #print(jsondumps)
        f.write(jsondumps+'\n')
    f.close()


def main(i):
    urlbasic='http://maoyan.com/board/4?offset='
    url=urlbasic+str(10*i)
    print(url)
    html=get_one_page(url)
    content=parse_one_page(html)
    #print(type(content))
    for i in [0,1,2,]:
        write_to_file(next(content))




if __name__ == '__main__':
    pool=Pool()
    pool.map(main, [i for i in range(0, 11)])


存储成如下格式的txt文件


Xpath语法(必须掌握)

首先我们来明确一下lxml和XPath的定义
lxml是一款高性能的 Python HTML/XML 解析器,我们可以利用XPath,来快速的定位特定元素以及获取节点信息
XPath (XML Path Language) 是一门在 HTML\XML 文档中查找信息的语言,可用来在 HTML\XML 文档中对元素和属性进行遍历。

那么 HTML和XML 有什么区别呢?

相对于BeautifulSoup相比 XPath是C语言实现哦 :)所以效率高哦


节点的概念:每个XML的标签我们都称之为节点



常用节点选择工具

Chrome插件 XPath Helper(下载crx扩展程序进行安装)

节点选择语法

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。


使用chrome插件选择标签时候,选中时,选中的标签会添加属性class="xh-highlight"

查找某个特定的节点或者包含某个指定的值的节点

选择未知节点

选取若干路径

lxml库

使用入门:
安装

pip3 install lxml

导入lxml 的 etree 库

from lxml import etree

利用etree.HTML,将html字符串转化为Element对象

html = etree.HTML(response.content) 

Element对象具有xpath的方法

html.xpath(xpath语句)

使用etree.tostring(html)将element对象转换成html文档

lxml 可以自动修正 html 代码

demo

from lxml import etree



text = ''' 
        <div> 
            <ul> 
                <li class="item-1"><a href="link1.html"></a></li> 
                <li class="item-1"><a href="link2.html">second item</a></li> 
                <li class="item-inactive"><a href="link3.html">third item</a></li> 
                <li class="item-1"><a href="link4.html">fourth item</a></li> 
                <li class="item-0"><a href="link5.html">fifth item</a> 
                # 注意,此处缺少一个 </li> 闭合标签 
            </ul> 
        </div> 

'''

# 使用etree,创建对象,把文本转换成elements对象
html = etree.HTML(text)

# 提取多条数据,需要对提取的结果进行遍历,或者说再次xpath
data_list = html.xpath('//div/ul/li/a')
# print(dir(data_list))
# print(data_list)
# 需要遍历的节点列表中如果没有数据,提取指定节点的数据会发生异常,
for data in data_list:
    # print(dir(data))
    # 三元表达式
    a_text = data.xpath('./text()')[0] if len(data.xpath('./text()')) > 0 else None
    a_href = data.xpath('./@href')[0]
    print(a_text,a_href)



print(html)
print(type(html))
print(dir(html))
# 返回的是对象
print(html.xpath('//div/ul/li[1]/a'))
# 提取链接,返回的是列表
print(html.xpath('//div/ul/li[1]/a/@href'))
# 提取文本,返回的是列表
print(html.xpath('//div/ul/li[1]/a/text()'))
# 提取文本,返回的是文本内容
print(html.xpath('//div/ul/li[1]/a/text()')[0])


百度校花吧爬取并保存图片案例

屏幕快照 2019-07-06 17.20.23.png
import requests
from lxml import etree
import os

class Baidu(object):

    def __init__(self, name):
        self.url = 'http://tieba.baidu.com/f?ie=utf-8&kw={}'.format(name)
        # 使用较老版本的请求头,该浏览器不支持js
        self.headers = {
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) '
        }

    # 发送请求,获取响应
    def get_data(self, url):
        response = requests.get(url,headers=self.headers)
        return response.content

    # 解析列表页数据,获取列表页面帖子的标题和链接
    def parse_list_page(self, data):
        with open('baidu.html','wb') as f:
            f.write(data)
        # 实例化etree对象
        html = etree.HTML(data)
        # 使用xpath语法,提取网页数据
        node_list = html.xpath("//*[@id='thread_list']/li[@class=' j_thread_list clearfix']/div/div[2]/div[1]/div[1]/a")
        # 判断获取结果
        # print(len(node_list))
        data_list = []
        # 遍历node_list
        for node in node_list:
            temp = {}
            temp['url'] = 'http://tieba.baidu.com' + node.xpath('./@href')[0]
            temp['title'] = node.xpath('./text()')[0]
            data_list.append(temp)

        # 提取下一页的节点
        next_node = html.xpath('//*[@id="frs_list_pager"]/a[last()-1]/@href')[0]
        # print(next_node)
        # 拼接下一页的完整url
        next_url = 'http:' + next_node
        # print(next_url)
        return data_list,next_url

    def parse_detail_page(self, data_list):
        html = etree.HTML(data_list)
        # 提取详情页面的图片链接
        image_list = html.xpath("//cc/div[contains(@class,'d_post')]/img[@class='BDE_Image']/@src")
        # 返回图片节点列表
        print(image_list)
        return image_list

    # 下载图片,保存图片文件
    # 创建文件夹
    def download(self, image_list):
        if not os.path.exists('images'):
            os.makedirs('images')
        for image in image_list:
            # os.sep在mac系统中是/,如果是windows系统,\\,跨平台
            file_name = 'images'+ os.sep + image.split('/')[-1]
            image_data = self.get_data(image)
            with open(file_name,'wb') as f:
                f.write(image_data)

    def run(self):
        # 构造url和请求头
        # 发送请求,获取响应
        next_url = self.url
        # 开启循环,
        while next_url:
            data = self.get_data(next_url)

            # 解析列表页数据,返回的列表数据、下一页的的数据
            data_list,next_url = self.parse_list_page(data)
            # 解析详情页的数据,获取详情页的图片的链接地址
            for data in data_list:
                url = data['url']
                result_list = self.get_data(url)
                image_list = self.parse_detail_page(result_list)
                # 保存数据,下载图片
                self.download(image_list)

if __name__ == '__main__':
    # 爬取百度校花吧所有图片并存在文件夹中
    baidu = Baidu('校花吧')
    baidu.run()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容