三、数据提取方法

1、基本概念

 数据提取就是从响应中获取我们想要的的数据的过程

数据分类:

非结构化数据:HTML等

  处理方法:正则表达式、xpath

结构化数据:json、xml等

  处理方法:转化为Python数据类型

 

2、正则表达式复习

 

 . 匹配 \n:

In [2]: re.findall(".","\n")
Out[2]: []

In [3]: re.findall(".","\n",re.DOTALL)
Out[3]: ['\n']

In [4]: re.findall(".","\n",re.S)
Out[4]: ['\n']

转义符号:

In [5]: re.findall("\.",".")
Out[5]: ['.']

[ ]只能匹配其中一个字符:

In [6]: re.findall("a[bcd]e","abe")
Out[6]: ['abe']

In [7]: re.findall("a[bcd]e","abce")
Out[7]: []
In [8]: re.findall("a[bcd]+e","abce")
Out[8]: ['abce']

非贪婪:

没匹配空格
In [19]: re.findall('<.+?>',a) Out[19]: ['<meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" />', '<meta name="description" content="提供图书、电影、音乐唱片的推荐、评论和价格比较,以及城市独特的文化生活。">', '<meta name="keywords" content="豆瓣,广播,登陆豆瓣">']
贪婪
In [24]: re.findall('<.+>',a,re.S) Out[24]: ['<meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" />\n<meta name="description" content="提供图书、电影、音乐唱片的推荐、评论和价格比较,以及城市独特的文化生活。">\n<meta name="keywords" content="豆瓣,广播,登陆豆瓣">']
非贪婪
In [25]: re.findall('<.+?>',a,re.S) Out[25]: ['<meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" />', '<meta name="description" content="提供图书、电影、音乐唱片的推荐、评论和价格比较,以及城市独特的文化生活。">', '<meta name="keywords" content="豆瓣,广播,登陆豆瓣">']

替换:

In [27]: b = "special flavor1234"

In [28]: re.sub("\d","_",b)
Out[28]: 'special flavor____'

compile提高效率

效率低
In [29]: re.sub("\d+","",b) Out[29]: 'special flavor'
传入正则 In [
30]: p = re.compile("\d") In [31]: p.findall(b) Out[31]: ['1', '2', '3', '4'] In [32]: p.sub("",b) Out[32]: 'special flavor'

参数要放到compile

In [33]: p = re.compile(".",re.S)

In [34]: p.findall("\n")
Out[34]: ['\n']

原始字符串r

In [38]: a = "a\nb"

In [39]: len(a)
Out[39]: 3

In [40]: a = r"a\nb"

In [41]: len(a)
Out[41]: 4
In [43]: r"a\nb"
Out[43]: 'a\\nb'

正则前加r的好处是,忽略转义符号的影响,使得前后一致

In [44]: re.findall("a\nb","a\nb")
Out[44]: ['a\nb']

In [45]: re.findall(r"a\nb","a\nb")
Out[45]: ['a\nb']

In [46]: re.findall(r"a\nb","a\\nb")
Out[46]: []

In [47]: re.findall(r"a\\nb","a\\nb")
Out[47]: ['a\\nb']

比如操作Windows文件对文件名中的 \ 可以加 r 处理

()起到过滤的效果,ab起到定位的效果

In [50]: re.findall("a(.*?)b","iuwrajkjsb")
Out[50]: ['jkjs']

 

3、正则表达式爬虫案例

爬取36kr网站数据

import requests
import re
import json


class ReSpider:

    def __init__(self):
        self.start_url = "https://36kr.com/"
        self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"}

    def parse_url(self, url):
        response = requests.get(url, headers=self.headers)
        return response.content.decode()

    def get_content_list(self, html_str):
        re_str = r"<a class=\"article-item-title weight-bold.*?rel=\"noopener noreferrer\">(.*?)</a>"
        content_list = re.findall(re_str, html_str, re.S)
        return content_list

    def save_content_list(self, content_list):
        with open("duanzi_list.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):  # 实现主要逻辑
        # 发送请求 获取响应
        html_str = self.parse_url(self.start_url)
        # 提取数据
        content_list = self.get_content_list(html_str)
        # 保存
        self.save_content_list(content_list)


if __name__ == '__main__':

    rs = ReSpider()
    rs.run()

内涵段子

# coding=utf-8
import requests
import re
import json

class Neihan:
    def __init__(self):
        self.start_url = "http://neihanshequ.com/"
        self.next_url_temp = "http://neihanshequ.com/joke/?is_json=1&app_name=neihanshequ_web&max_time={}"
        self.headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"}

    def parse_url(self,url):#发送请求
        print(url)
        response = requests.get(url,headers=self.headers)
        return response.content.decode()

    def get_first_page_content_list(self,html_str): #提取第一页的数据
        content_list = re.findall(r"<h1 class=\"title\">.*?<p>(.*?)</p>",html_str,re.S)
        max_time = re.findall("max_time: '(.*?)',",html_str)[0]
        return content_list,max_time

    def save_content_list(self,content_list): #保存
        with open("neihan.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 get_content_list(self,json_str): #提取从第二页开始的json中的数据
        dict_ret = json.loads(json_str)
        data = dict_ret["data"]["data"]
        content_list = [i["group"]["content"] for i in data]
        max_time = dict_ret["data"]["max_time"]
        has_more = dict_ret["data"]["has_more"]
        return content_list,max_time,has_more

    def run(self):#实现主要逻辑
        #1.start_url
        #2.发送请求,获取响应
        html_str = self.parse_url(self.start_url)
        #3.提取数据
        content_lsit,max_time = self.get_first_page_content_list(html_str)
        #4.保存
        self.save_content_list(content_lsit)
        has_more = True #有第二页
        while has_more:
            #5.构造下一页的url地址
            next_url = self.next_url_temp.format(max_time)
            #6.发送请求,获取响应
            json_str = self.parse_url(next_url)
            #7.提取数据,提取max_time
            content_lsit,max_time,has_more = self.get_content_list(json_str)
            #8.保存
            self.save_content_list(content_lsit)
            #9.循环5-8步



if __name__ == '__main__':
    neihan = Neihan()
    neihan.run()

 

4、json

json是一种轻量级的数据交换格式,适用于如前端后端数据交互等场景

(一般可以在网页的手机版中找到json,网页中的数据不一定都来自json,有可能来自js生成)

 

 

import json
import requests
from pprint import pprint

url = "https://36kr.com/pp/api/aggregation-entity?type=web_latest_article&b_id=71663&per_page=10"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"}

response = requests.get(url, headers=headers)
json_str = response.content.decode()

# 把json类型转化为python类型(字典)
ret1 = json.loads(json_str)
print(type(ret1))
pprint(ret1)  # pretty print 使输出看起来更好看

# 把python类型转化为json字符串
with open("31kr.txt", "w", encoding='utf-8') as f:
    # 1.字符串才能写入
    # 2.ensure_ascii默认以ascii编码来转换,因此要改成false
    # 3.默认json字符串为一行,indent使得在保存时每一级会有2个空格的缩进
    # 4.使用str() eval()都不能完美的转换
    f.write(json.dumps(ret1, ensure_ascii=False, indent=2))

json使用注意点:

json中字符串都是双引号引起来的

  如果不是双引号,可以使用eval来实现简单的字符串和Python类型的转换

  如果字符串太复杂,可使用replace替换成双引号

 

类文件对象:具有read或write方法的对象就是类文件对象

# 使用json.load提取类文件对象中的数据
with open("31kr.txt", "r", encoding='utf-8') as f:
    ret2 = json.load(f)
    print(ret2)

# json.dump能够把python类型放入类文件对象中
with open("32kr.txt", "w", encoding='utf-8') as f:
    json.dump(ret1, f, ensure_ascii=False, indent=2)

 

36kr新闻抓取

import re
import requests
import json

url = "https://36kr.com"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"}

html_str = requests.get(url, headers=headers).content.decode()

ret = re.findall("\"homeData\":(.*?),\"channel\"", html_str)[0]

# with open("1.txt", "w", encoding='utf-8') as f:
#     f.write(ret)
# json格式错误,先写入文件查看,在右下角定位到报错的列

content = json.loads(ret)
print(content)

 

5、豆瓣爬虫练习

(老师的例子,现在的url无法实现爬取)

import requests
import json


class DoubanSpider(object):

    def __init__(self):
        self.url_temp = "https://m.douban.com/rexxar/api/v2/subject_collection/tv_american/items?os=ios&for_mobile=1&callback=jsonp1&start={}&count=18"
        self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"}

    def parse_url(self, url):
        response = requests.get(url, headers=self.headers)
        return response.content.decode()

    def get_content_list(self, json_str):
        ret_dict = json.loads(json_str)
        content_list = ret_dict['subject_objection_items']
        total = ret_dict['total']
        return content_list, total

    def save_content(self, content_list):
        with open("douban.txt", "a", encoding='utf-8') as f:
            for content in content_list:
                f.write(json.dumps(content))
                f.write("\n")

    def run(self):
        num = 0
        total = 100  # 先给一个值,假设有第一页
        while num < total+18:
            # 准备url
            url = self.url_temp.format(num)
            # 发送请求,获取响应
            json_str = self.parse_url(url)
            # 提取数据
            content_list, total = self.get_content_list(json_str)
            # 保存
            self.save_content(content_list)
            # 循环结束方法1
            # if len(content_list) < 18:
            #     break
            # 构造下一页的url地址,进入循环
            num += 18


if __name__ == '__main__':
    ds = DoubanSpider()
    ds.run()

 

6、XPATH和XML

xpath是一门在HTML/XML文档中查找信息的语言,利用xpath,可以快速定位特定元素

xpath使用路径表达式来选区XML文档中的节点或者节点集,和电脑文件系统中的表达式比较相似。

 

 从标签中提取文本

/html/head/title/text()

获取标签的属性

/html/head/link/@href

直接选择文档中所有li标签,而不考虑它们的位置

//li

选择特定id的ul

//ul[@id="detail-list"]/li

直接选中li中的文本而不用一层一层选

//ul[@id="detail-list"]/li//p/text()

li下任何一个a标签

li//a

获取a下的所有标签的文本

a//text()

获取指定文本内容的标签

//a[text()="下一页"]/@href

 

posted on 2019-10-26 16:28  三分天涯  阅读(201)  评论(0编辑  收藏  举报