欢迎关注 『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爬虫』 专栏,持续更新中
『未完待续』


Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐