爬虫进阶

数据提取方法

json

  • 数据交换格式(后端转换到前端),看起来像python类型(字典、集合…)的字符串
  • 使用json之前需要导入模块
  • 哪里会返回json数据
    • 浏览器切换到手机版
    • 抓包app
  • jsom.loads
    • 把json字符串转化为python类型
    • json.loads('json字符串')
  • json.dump
    • 把python类型转化为json字符串
    • json.dumps('{python字典}')
    • json.dumps(ret1,ensure_ascii=False,indent=2)
      • ensure_ascii : 让中文显示成中文
      • indent: 能够让下一行在上一行的基础上空格, indent = 2
  • 豆瓣电视爬虫案例
    • preview搜索需要的爬取的内容
    • Filter 过滤显示输入字段的url地址
    • 案例实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#coding=gbk
from parse import parse_url
import json

class DoubanSpider:
"""豆瓣内容抓取"""
def __init__(self):
#最开始的入口
self.temp_url = "https://movie.douban.com/j/search_subjects?type=tv&tag=%E7%BE%8E%E5%89%A7&sort=recommend&page_limit=20&page_start={}" # {}大括号里对应字段传入值num的.format(num)

def get_content_list(self,html_str): #提取数据
dict_data = json.loads(html_str) #有callback就删除
content_list = dict_data["subjects"]
#total = dict_data["total"] 没有total的话自己设置
return content_list #,total

def save_content_list(self,content_list):
with open("douban.json","a") as f:
for content in content_list:
f.write(json.dumps(content,ensure_ascii=False))
f.write("\n")
print('保存成功')

def run(self): #实现主要逻辑
num = 0
#total = 100 初始化total,第二页更新时不使用
while num < 100: #num < total+ page_limit 全部提取数据
#1.start_url
url = self.temp_url.format(num) #对start进行复制
print(url)
#2.发送请求,获取响应
html_str = parse_url(url)
#3.提取数据
content_list = self.get_content_list(html_str) #content,tatal =
#4.保存
self.save_content_list(content_list)
#5.构造下一页的url,循环2-5步
num += 20

if __name__ == '__main__':
douban = DoubanSpider() #实例化
douban.run()

xpath和lxml

xpath

  • 一门从html中提取数据的语言
    • xpath语法
  • xpath helper插件:帮助我们从element中定位数据
    • 1.选择节点(标签)
    • /html/head/meta: 能够选中html下的所有meta标签
    • 2.// : 能够选择任意标签
    • //li: 当前页面上所有li标签
    • /html/meta//link: 结合使用显示meta下的link标签
    • 3.@符号用途
    • 选择具体属性值
    • //div[@class='feed-infinite-wrapper']/ul/li: 选择class=’feed-infinite-wrapper’的div下的ul下的li
    • //div[@class='feed-infinite-wrapper']/ul/li/a/@href:选择a标签的href值
    • 4.获取a标签下的文本: /a/text()
    • 标签下的所有文本: /a//text()
    • 5.点前
    • “./a” 当前节点下的a标签

lxml

  • 安装: pip install lxml
    • 使用:
      from lxml import etree
      element = etree.HTML(‘接受的html字符串’) #转化为element对象
      element.xpath(“”) #url地址对应响应,网页源码,只有elements和url响应相同时才能看elements
  • 实现代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #coding=gbk
    import requests
    from lxml import etree

    url = "https://movie.douban.com/chart"
    headers = {
    "User_Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
    }

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

    #print(html_str)

    #使用etree处理数据
    html = etree.HTML(html_str)
    #print(html)

    #1.获取所有电影图片地址
    img_list = html.xpath("//div[@class='indent']/div/table//a[@class='nbg']/img/@src")
    #print('movie_img地址: \n\t'+str(img_list)

    #2.获取所有电影的url地址
    url_list = html.xpath("//div[@class='indent']/div/table//div[@class='pl2']/a/@href")
    #print(url_list)

    #3.每部电影元素组成字典,字典中包含标题、url、图片地址、评论数、评分
    #思路:
    #(1)分组
    #(2)每一组提取数据

    ret1 = html.xpath("//div[@class='indent']/div/table")
    #print(ret1) #这是一个列表式element对象,即标签节点

    for table in ret1:
    #当[0]为空时会报错,先判断是否为null
    item = {} #空集合
    item['title'] = table.xpath(".//div[@class='pl2']/a/text()")[0].replace('/','').strip()#当前节点下续写
    item['href'] = table.xpath(".//div[@class='pl2']/a/@href")[0]
    item['img'] = table.xpath(".//a[@class='nbg']/img/@src")[0]
    item['comment_num'] = table.xpath(".//span[@class='pl']/text()")[0]
    item['rating_num'] = table.xpath(".//span[@class='rating_nums']/text()")[0]
    #列表推导式
    #item["title"] = [for i in item["title"]] #正则
    print(item)

xpath和lxml案例(糗事百科)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#coding=gbk
from lxml import etree
import requests
import json

class QiubaiSpader:
def __init__(self):
self.url_temp = "https://www.qiushibaike.com/8hr/page/{}/"
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
}

def get_url_list(self): #根据url地址规律构造url_list
url_list = [self.url_temp.format(i) for i in range(1,14)]
return url_list

def parse_url(self,url): #发送请求获取响应
print('now_parse:'+url)
response = requests.get(url,headers=self.headers)
return response.text.encode('GBK','ignore').decode('gbk')

def get_content_list(self,html_str): #从字符串响应中提取数据
html = etree.HTML(html_str)
content_list = []
#1.分组
div_list = html.xpath('//div[@id="content-left"]/div')
for div in div_list:
item = {}
#获取的字符串[0]指的是去除两边换行\n,取除字符串
item["author_name"] = div.xpath(".//h2/text()")[0].strip() if len(div.xpath(".//h2/text()"))>0 else None #提取用户名
item["author_img"] = div.xpath(".//img/@src")
#内容
item["content"] = div.xpath('.//div[@class="content"]/span/text()') #<br>将字符串隔断
item["content"] = [i.strip() for i in item["content"]]
item["stats_vote"] = div.xpath('.//span[@class="stats-vote"]/i/text()')
#好笑数
item["stats_vote"] = item["stats_vote"][0] if len(item["stats_vote"])>0 else None
item["stats-comments"] = div.xpath('.//span[@class="stats-comments"]//i/text()')
#评论数
item["stats-comments"] = item["stats-comments"][0] if len(item["stats-comments"])>0 else None
item["content_img"] = div.xpath('.//div[@class="thumb"]//img/@src')
#内容图片不完整加上获取
item["content_img"] = 'https:' + item["content_img"][0] if len(item["content_img"])>0 else None
content_list.append(item)
return content_list #返回json格式

def save_content_list(self,content_list):#保存
with open('qiubai.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地址规律来构造url列表
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)
#4.保存
self.save_content_list(content_list)

if __name__ == '__main__':
qiubai = QiubaiSpader()
qiubai.run()

写爬虫的套路

  • 1.url
    • 知道url地址规律和总页码: 构造url地址的列表
    • start_url、先构造起始url地址,然后构造下一页面
  • 2.发送请求获取响应
    • request(parse方法)
  • 3.提取数据
    • 返回json字符串:json模块
    • 返回的是html字符串:lxml模块配合xpath提取数据
  • 4.保存
    • 保存到文本,或者数据库

基础知识点的学习

  • format:字符串格式化一种方式
1
2
3
4
"传智{}播客".format("1")
"传智{}播客".format(1)
"传智{}播客".format([1,2,3])
"传智{}播客{}".format({1,2,3},[1,2,3])
  • 列表推导式
    • 帮助我们快速的生成包含一对数据的列表
      [i+10 for i in range(10)] —> [10,11,12…19]
      ["10月{}日".format(i) for i in range(1,10)] —> [“10月1日”,”10月2日”,..,”10月9日”]
  • 字典推导式
    • 帮助我们快速的生成包含一对数据的字典
1
2
{i+10:i for i in range(10)} #{10:0,11:1..10:9}
{"a{}".format(i):10 for i in range(3)} # {"a0":10,"a1":10,"a2":10}
  • 三元运算符
    • if去前面的条件成立就把if前面的结果赋值给a,否则把else后面的结果赋值给a
a = 10 if 4 > 3 else 20   # a = 10
a = 10 if 4 < 3 else 20   # a = 20
文章目录
  1. 1. 数据提取方法
    1. 1.1. json
    2. 1.2. xpath和lxml
      1. 1.2.1. xpath
      2. 1.2.2. lxml
      3. 1.2.3. xpath和lxml案例(糗事百科)
    3. 1.3. 写爬虫的套路
    4. 1.4. 基础知识点的学习
,