『scrapy爬虫』09. 获取子页面的数据(详细注释步骤)
·
欢迎关注 『scrapy爬虫』 专栏,持续更新中
欢迎关注 『scrapy爬虫』 专栏,持续更新中
前面我们爬取了top250的电影名字,评分和一句话影评,如果我们想要得到每个电影的主题和时长,需要前往每个电影对应的子页面.
修改数据表结构
我这边是直接删掉原来的表新建表.新增了时长和简介这两个字段.
USE mzh_scrapy;
DROP TABLE IF EXISTS tb_top_movie;
CREATE TABLE tb_top_movie (
`mov_id` INT UNSIGNED AUTO_INCREMENT COMMENT '编号',
`title` VARCHAR(50) NOT NULL COMMENT '标题',
`score` DECIMAL(3,1) NOT NULL COMMENT '评分',
`quato` VARCHAR(200) DEFAULT '' COMMENT '评价',
`duration` int not null COMMENT '时长',
`intro` VARCHAR(10000) DEFAULT '' COMMENT '简介',
PRIMARY KEY (`mov_id`)
) ENGINE=InnoDB COMMENT='top电影表';

增加数据的字段(items.py)
import scrapy
class MovieItem(scrapy.Item):
title=scrapy.Field()
score=scrapy.Field()
quato=scrapy.Field()
duration=scrapy.Field()
intro=scrapy.Field()
修改数据管道(pipelines.py)
就是在原来的基础上新增数据的字段
import openpyxl
import pymysql
#用于将数据存入mysql的类
class DBPipeline:
# 初始化
def __init__(self):
self.conn=pymysql.connect(host='127.0.0.1',port=3306,
user='root',passwd='123456',database='mzh_scrapy',charset='utf8mb4')
self.cursor=self.conn.cursor()
self.data=[]#准备一个存放数据的容器
# 开始爬虫时候要进行的操作
def open_spider(self, spider):
pass
# 处理爬取到的数据并进行后续处理
def process_item(self, item, spider):
title=item.get('title',"")#如果没有获取到标题,默认空
score=item.get('score',0)#如果没有获取到评价默认0 因为我们数据是 int
quato=item.get('quato',"")#如果没有获取到评价默认空
duration=item.get('duration',0)#如果没有获取到评价默认0 因为我们数据是 int
intro=item.get('intro',"")#如果没有获取到评价默认空
self.data.append((title,score,quato,duration,intro)) #将得到的一行的电影相关数据放入列表
if len(self.data)>=100:
self._write_to_db()
return item #为什么是return? 我们要让这个管道先,return会把item数据传递给下一个管道用于保存excel的
def _write_to_db(self):
# execute->改为了 executemany 支持多条数据批量传入数据库
self.cursor.executemany(
'insert into tb_top_movie(title,score,quato,duration,intro) values (%s,%s,%s,%s,%s)', self.data
)
self.conn.commit() # 把数据缓冲区的数据提交到数据库
self.data.clear() # 每次添加后清空data 避免重复添加数据
# 关闭爬虫时候要进行的操作
def close_spider(self, spider):
if len(self.data) >= 0:#如果还有残留的数据,但是因为不满100条没有传到数据库也要做好处理
self._write_to_db()
self.conn.close()
# 用于将数据存入excel的类
class MyscrapyPipeline:
# 初始化我们的excel文件
def __init__(self):
self.wb=openpyxl.Workbook()
self.ws=self.wb.active #拿到默认的被激活的工作表
self.ws.title="top250" #工作表的名字改为 top250
# self.ws.append(("标题","评分","主题")) #增加表头
self.ws.append(("标题","评分","影评",'时长','简介')) #增加表头
# 开始爬虫时候要进行的操作
def open_spider(self,spider):
pass
# 关闭爬虫时候要进行的操作
def close_spider(self,spider):
#保存Excel文件
self.wb.save("电影数据.xlsx")
#关闭工作簿(Workbook)和工作表(Worksheet)
self.wb.close()
# 处理爬取到的数据 item是我们前面yeild返回的数据
def process_item(self, item, spider):
title=item.get("title","")
score=item.get("score","")
quato=item.get("quato","")
duration=item.get("duration","")
intro=item.get("intro","")
self.ws.append((title,score,quato,duration,intro))#将得到的数据一行写入excel
return item
修改获取数据的爬虫(douban.py)
首先拿到简介子页面的url
# 简介子页面的 Selector
# content > div > div.article > ol > li:nth-child(1) > div > div.info > div.hd > a
detail_url=myselector.css("div.info > div.hd > a::attr(href)").extract_first()
# print(detail_url)

# 时长的选择器
# #info > span:nth-child(20) 因为这里span的数量可能变化,所以更换思路
# <span property="v:runtime" content="142">142分钟</span>
movie_item['duration']=myselector.css('span[property="v:runtime"]::attr(content)').extract_first() or ""

# 简介的选择器
# #link-report-intra > span.short > span
# <span property="v:summary">简介内容</span>
movie_item['intro']=myselector.css('span[property="v:summary"]::text').extract_first() or ""

分布解析
注意callback=self.parse这个是默认的钩子函数流程,你不写也是这样,所以我们之前在parse中编写获取前面三个字段的数据.
def start_requests(self) :
for page in range(1): #10页
yield Request(
url=f'https://movie.douban.com/top250?start={page*25}&filter=',
# meta={'proxy':"socket5://127.0.0.1:1086"},#socket5代理
# meta={'proxy':"http://127.0.0.1:1086"}#购买的商业代理一般是http给一个api接口
callback=self.parse# 这一行是系统默认带有的,你不写也是默认这样
)
通过yield Request 在parse后进行二次的解析
def parse(self,response, **kwargs):
# 省略~~~~~
# yield movie_item#把整理得到的数据给管道
yield Request(url=detail_url, #传递要前往的子页面url,用于新的解析
callback=self.parse_detail,#钩子函数,指定下一个要执行的解析parse
cb_kwargs={'item': movie_item}#传递数据参数
)
最终douban.py
注意,为了快速测试,我们这里把range循环10改成了1,也就是只爬取第一页的数据.
import scrapy
from scrapy import Selector,Request
from myscrapy.items import MovieItem
class DoubanSpider(scrapy.Spider):
name = "douban"
allowed_domains = ["movie.douban.com"]# 限制或允许访问的域名列表
start_urls = ["https://movie.douban.com/top250"] # 起始url
def start_requests(self) :
for page in range(1): #10页
yield Request(
url=f'https://movie.douban.com/top250?start={page*25}&filter=',
# meta={'proxy':"socket5://127.0.0.1:1086"},#socket5代理
# meta={'proxy':"http://127.0.0.1:1086"}#购买的商业代理一般是http给一个api接口
# callback=self.parse# 这一行是系统默认带有的,你不写也是默认这样
)
def parse(self,response, **kwargs):
myselector=Selector(text=response.text)
# 拿到了所有的li,也就是所有的电影,每一个li代表一个电影,list_items是由250个电影li组成的list
list_items=myselector.css("#content > div > div.article > ol > li")
for list_item in list_items:
# 简介子页面的 Selector
# content > div > div.article > ol > li:nth-child(1) > div > div.info > div.hd > a
detail_url=myselector.css("div.info > div.hd > a::attr(href)").extract_first()
# print(detail_url)
movie_item=MovieItem()#新建类的对象
# 电影标题的 Selector
# content > div > div.article > ol > li:nth-child(1) > div > div.info > div.hd > a > span:nth-child(1)
movie_item['title']=list_item.css("span.title::text").extract_first() # extract_first()从选择器中提取第一个匹配的数据。
# 电影评分
# content > div > div.article > ol > li:nth-child(1) > div > div.info > div.bd > div > span.rating_num
movie_item['score']=list_item.css("span.rating_num::text").extract_first() # extract_first()从选择器中提取第一个匹配的数据。
# # 电影影评
# content > div > div.article > ol > li:nth-child(1) > div > div.info > div.bd > p.quote > span
movie_item['quato']=list_item.css("span.inq::text").extract_first() # extract_first()从选择器中提取第一个匹配的数据。
# yield movie_item#把整理得到的数据给管道
yield Request(url=detail_url, #传递要前往的子页面url,用于新的解析
callback=self.parse_detail,#钩子函数,指定下一个要执行的解析parse
cb_kwargs={'item': movie_item}#传递带过去的数据参数
)
def parse_detail(self,response, **kwargs):
movie_item=kwargs['item']#拿到前面得到的参数
myselector=Selector(text=response.text)
# 时长的选择器
# #info > span:nth-child(20) 因为这里span的数量可能变化,所以更换思路
# <span property="v:runtime" content="142">142分钟</span>
movie_item['duration']=myselector.css('span[property="v:runtime"]::attr(content)').extract_first() or ""
# 简介的选择器
# #link-report-intra > span.short > span
# <span property="v:summary">简介内容</span>
movie_item['intro']=myselector.css('span[property="v:summary"]::text').extract_first() or "没有简介"
yield movie_item #把所有5个字段传回

总结
大家喜欢的话,给个👍,点个关注!给大家分享更多计算机专业学生的求学之路!
版权声明:
发现你走远了@mzh原创作品,转载必须标注原文链接
Copyright 2024 mzh
Crated:2024-3-1
欢迎关注 『scrapy爬虫』 专栏,持续更新中
欢迎关注 『scrapy爬虫』 专栏,持续更新中
『未完待续』
更多推荐
所有评论(0)