scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

06-01 1461阅读

一、环境准备

1、python 

python3以上,你可以直接下个anaconda,参考之前我写的博客

Anaconda安装+scrapy部署及初步认识-CSDN博客

2、mysql

3、scrapy

参考:Anaconda安装+scrapy部署及初步认识-CSDN博客

4、selenium+chromdriver

因为我的浏览器是chrom,所以用的chromdiver

参考:scrapy爬虫实战:爬取财经网站新闻数据(动态渲染页面)---详细图文解说-CSDN博客

scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

二、实战

1、项目介绍

本项目要爬取的数据是财联社的头条新闻数据,模拟用户点击加载更多按钮,获取完整的网页源码,并把数据存储在mysql数据库。

scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

财联社是主流的财经新闻媒体,专注于中国证券市场动态的分析、报道。

财联社深度:重大政策事件及时分析解读_供给侧改革

scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

然后按F12分析发现本网页呈现的逻辑:列表数据是通过Ajax加载的。

数据通过 AJAX 加载 是指网页的内容(如列表、表格、动态更新的信息)不是直接写在初始 HTML 代码中,而是通过 JavaScript 异步请求(AJAX) 从服务器获取数据,再动态插入到页面中的技术。

scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

要想爬取全部头条数据得使用selenium模拟人工点击到加载完成才能获取。

2、创建项目

新建项目

 首先新建一个Scrapy项目,名字叫做financeSpider,创建命令如下(在base环境中执行):

(看过上个博客已经创建过项目的不需要再创建)

scrapy startproject financeSpider

接下来进入项目,然后新建一个Spider,名称cls_finance,命令如下:

cd ./financeSpider
scrapy genspider cls_finance https://www.cls.cn/

定义Item(items) 

定义需要爬取的字段,在items.py里面定义一个FinancespiderItem,代码如下(跟上个博客的数据结构一样,可以不用再定义)

class FinancespiderItem(scrapy.Item):
    source=scrapy.Field()
    source_url= scrapy.Field()
    title = scrapy.Field()
    link = scrapy.Field()
    content = scrapy.Field()
    update_time = scrapy.Field()

mysql建表

建表并给url列设为唯一,ddl命令如下

CREATE TABLE if not exists finance_news (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,  -- ✅ 自增ID(无符号整数)
    url VARCHAR(255) NOT NULL UNIQUE COMMENT '文章url',            -- ✅ 唯一URL(
    title VARCHAR(255) COMMENT '文章标题',
    source VARCHAR(255) COMMENT '网站来源网站',
    source_url VARCHAR(255) COMMENT '来源url',
    content TEXT COMMENT '文章内容',
    update_time VARCHAR(255) COMMENT '文章更新时间',
    dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '数据写入时间',  -- ✅ 时间戳(自动记录时间)
    -- 可选:添加普通索引(根据查询需求)
    INDEX idx_dt (dt)  -- 如果经常按时间查询可加索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
# 加唯一索引
ALTER TABLE finance_news
ADD UNIQUE INDEX idx_unique_url (url);

3、selenium模拟用户点击网页并加载更多(middlewares)

在middlewares文件中,新建selenium下载中间件(middlewares),完整代码如下

class SeleniumMiddleware:
    def process_request(self, request, spider):
        # 检查是否需要使用Selenium,默认关闭
        if not request.meta.get('use_selenium', False):
            return None  # 继续正常处理
        # 配置浏览器参数
        chrome_options = Options()
        # chrome_options.add_argument("--ignore-certificate-errors")  # 忽略 SSL 证书错误
        # chrome_options.add_argument('--ignore-ssl-errors')
        chrome_options.add_argument('--headless')  # 无头模式
        driver = webdriver.Chrome(options=chrome_options)
        try:
             driver.get(request.url)
             click_attempts = 0  # 点击尝试计数器
             max_clicks = 10  # 最大点击次数(防死循环)
             while click_attempts  

代码重点解析:

  • 模拟点击加载更多按钮,因为要点击多次设置了while循环,以防发生死循环所以做了个最大值限制

    scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

    • selenium耗内存,下载效率慢,所以在request设置了开启和关闭标签,

      scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

      • 一定要记得及时关闭浏览器,释放内存

         scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

        4、管道文件(Pipelines)

        Pipeline文件,把数据写入mysql数据库(同上一篇博客)

        import pymysql
        from itemadapter import ItemAdapter
        from scrapy.exceptions import DropItem
        class MySQLPipeline:
            def __init__(self, host, port, user, password, db, charset):
                self.host = host
                self.port = port
                self.user = user
                self.password = password
                self.db = db
                self.charset = charset
                self.connection = None
                self.cursor = None
            @classmethod
            def from_crawler(cls, crawler):
                return cls(
                    host=crawler.settings.get('MYSQL_HOST'),
                    port=crawler.settings.get('MYSQL_PORT'),
                    user=crawler.settings.get('MYSQL_USER'),
                    password=crawler.settings.get('MYSQL_PASSWORD'),
                    db=crawler.settings.get('MYSQL_DATABASE'),
                    charset=crawler.settings.get('MYSQL_CHARSET')
                )
            def open_spider(self, spider):
                """连接数据库"""
                self.connection = pymysql.connect(
                    host=self.host,
                    port=self.port,
                    user=self.user,
                    password=self.password,
                    db=self.db,
                    charset=self.charset,
                    cursorclass=pymysql.cursors.DictCursor
                )
                self.cursor = self.connection.cursor()
            def close_spider(self, spider):
                """关闭连接"""
                self.connection.close()
            def process_item(self, item, spider):
                """处理Item"""
                try:
                    # 构建SQL语句(根据你的表结构修改,按key值进行更新数据)
                    sql = """
                        INSERT INTO finance_news(
                            url,
                            title,
                            source,
                            source_url,
                            content,
                            update_time
                        ) VALUES ( %s, %s, %s, %s, %s , %s )
                        ON DUPLICATE KEY UPDATE 
                        title = VALUES(title), 
                        source = VALUES(source),
                        source_url = VALUES(source_url), 
                        content = VALUES(content),
                        update_time = VALUES(update_time);"""
                    params = (item["link"],item["title"],item["source"],item["source_url"],item["content"],item["update_time"])
                    # 从item提取数据(字段名需要对应)
                    self.cursor.execute(sql,params)
                    self.connection.commit()
                except Exception as e:
                    self.connection.rollback()
                    raise DropItem(f"Error saving item to MySQL: {str(e)}")
                return item
        

        5、配置文件(settings)

        释放DOWNLOADER_MIDDLEWARES

        DOWNLOADER_MIDDLEWARES = {
           "financeSpider.middlewares.FinancespiderSpiderMiddleware": 543,#原来的
           "financeSpider.middlewares.SeleniumMiddleware": 600          #新建的selenium中间件
        }

        级别越低优先级越高

        完整代码如下(其他配置同上一篇博客):

        BOT_NAME = "financeSpider"
        SPIDER_MODULES = ["financeSpider.spiders"]
        NEWSPIDER_MODULE = "financeSpider.spiders"
        #修改请求头,可以弄得完整点,这个是全局配置,spider请求的时候不需要加
        # Override the default request headers:
        DEFAULT_REQUEST_HEADERS = {
            'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
            'accept-encoding': 'gzip, deflate, br, zstd',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
        #下载中间件
        DOWNLOADER_MIDDLEWARES = {
           "financeSpider.middlewares.FinancespiderSpiderMiddleware": 543,
           "financeSpider.middlewares.SeleniumMiddleware": 600          
        }
        #mysql的配置参数根据自己的mysql地址修改
        # mysql SETTING========
        MYSQL_HOST='192.168.X.XX'
        MYSQL_PORT=3306
        MYSQL_USER='root'
        MYSQL_PASSWORD='123456'
        MYSQL_DATABASE = 'finance'
        MYSQL_CHARSET='utf8mb4'
        # Configure item pipelines
        # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
        ITEM_PIPELINES = {
            # "financeSpider.pipelines.FinanceSpiderPipeline": 300,
            #根据自己的管道文件名称修改
            "financeSpider.pipelines.MySQLPipeline": 300,
        }
        #推荐加上的配置参数
        ROBOTSTXT_OBEY = False
        RETRY_HTTP_CODES = [401, 403, 500, 502, 503, 504]
        CONCURRENT_REQUESTS = 10
        

        6、爬虫文件(cls_finance) 

        import scrapy
        import time
        from bs4 import BeautifulSoup
        from financeSpider.items import FinancespiderItem
        # 爬取财联社的头条新闻 https://www.cls.cn/depth?id=1000  使用selenium + Beautifulsoup技术  
        class ClsFinanceSpider(scrapy.Spider):
            name = "cls_finance"
            allowed_domains = ["cls.cn"]
            start_urls = ["https://www.cls.cn/"]
            #因为要使用selenium重写请求首页
            def start_requests(self):
                url="https://www.cls.cn/depth?id=1000"
                yield scrapy.Request(url=url,meta={"use_selenium":True},callback=self.parse) 
        # meta={"use_selenium":True} 只有需要selenium的时候标记,默认不用
            def parse(self, response):
                soup = BeautifulSoup(response.text, 'lxml')
                # print (soup)
                # 用css模糊匹配
                items = soup.select('div[class*="subject-interest-list"]')
                # print (items)
                for t in items:
                    item = FinancespiderItem()
                    item['source'] = '财联社'
                    item['source_url'] = response.url
                    item['title'] = t.select_one('div[class*="subject-interest-title"]').text
                    item['link'] = "https://www.cls.cn" + t.a['href']
                    print(item['link'] )
                    print (item['title'])
                    time.sleep(1) #避免爬取太频繁
                    yield scrapy.Request(url=item['link'], meta={"item": item}, callback=self.parse1)
            def parse1(self, response):
                item = response.meta['item']
                soup = BeautifulSoup(response.text, 'lxml')
                print("正在抓取内容")
                try:
                    item["content"] = soup.select_one('div[class*="content-left"]').text.strip()
                    item['update_time'] = soup.select_one('div[]').text.strip()[0:16]
                except AttributeError as e:
                    print ("AttributeError:获取文章文本内容解析报错")
                # print  (item["content"] +"\t"+item['update_time'])
                yield item
        

        7、运行测试

        在base环境,写入命令

        scrapy crawl cls_finance

        运行结果如下:

        scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

        最后运行完的结果 : 

        scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

        101条新闻全部爬取成功。

        数据结果如下:

        scrapy爬虫实战(二): 结合Selenium实现动态加载网页数据采集(详细解说爬取过程以及完整代码)

        我们已经可以用scrapy+selenium成功爬取数据啦!

        ………………………………………………………………………………………………………………

        大家有什么优化或者建议欢迎评论区告诉我,我们一起共同进步,谢谢!

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码