学习
实践
活动
专区
工具
TVP
写文章
专栏首页muller的测试分享使用 Scrapy + Selenium 爬取动态渲染的页面
原创

使用 Scrapy + Selenium 爬取动态渲染的页面

在通过scrapy框架进行某些网站数据爬取的时候,往往会碰到页面动态数据加载的情况发生,如果直接使用scrapy对其url发请求,是绝对获取不到那部分动态加载出来的数据值。但是通过观察我们会发现,通过浏览器进行url请求发送则会加载出对应的动态加载出的数据。那么如果我们想要在scrapy也获取动态加载出的数据,则必须使用selenium创建浏览器对象,然后通过该浏览器对象进行请求发送,获取动态加载的数据值. 本文分享scrapy的介绍和如何配合selenium实现动态网页的爬取。

Scrapy

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了 页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。

Scrapy 安装并运行

安装 通过pip install Scrapy 安装即可, Ubuntu安装需要安装依赖sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zliblg-dev libffi-dev libssl-dev;

创建项目: scrapy startproject project_name;

创建爬虫: 进入项目根目录 scrapy genspider spider_name sprder_domian;

目录介绍;

project_folder -- 项目文件夹名称
|
|──project_name -- 该项目的python模块,一般和项目文件夹名称相同
|  |
|  |──spider -- 放置spider代码的包,以后所有的爬虫,都存放在这个里面
|  |
|  |──items.py -- 用来存放爬虫怕写来的数据的模型
|  |
|  |──middlewares.py -- 用来存放各种中间件的文件
|  |
|  |──pipelines.py -- 用来对items里面提取的数据做进一步处理,如保存到本地磁盘等
|  |
|  |──settings.py -- 本爬虫的一些配置信息(如请求头、多久发送一次请求、ip代理池等)
|
|──scrapy.cfg -- 项目的配置文件

01

Scrapy执行流程

Scrapy中的数据流由执行引擎控制,其过程如下:

  1. (从第二步)重复直到调度器中没有更多的请求(Requests)。

02

Scrapy架构图

03

中间件架构

Selenium

Selenium有很多东西,但从本质上讲,它是一个 Web 浏览器自动化工具集,它使用可用的最佳技术远程控制浏览器实例并模拟用户与浏览器的交互。它允许用户模拟最终用户执行的常见活动;在字段中输入文本,选择下拉值和复选框,并单击文档中的链接。它还提供了许多其他控件,例如鼠标移动、任意 JavaScript 执行等等。

01

selenium 安装

安装 pip install selenium

02

驱动安装

使用selenium驱动chrome浏览器需要下载chromedriver,而且chromedriver版本需要与chrome的版本对应,版本错误的话则会运行报错。

03

chrome

下载chromedriver可以通过淘宝镜像地址:

http://npm.taobao.org/mirrors/chromedriver/

最新的镜像与Chrome同名,尽量选择版本相近的避免兼容问题,镜像下notes.txt可查看当前驱动支持的版本。

04

其他浏览器驱动

Opera:http://npm.taobao.org/mirrors/operadriver/
IE:   http://selenium-release.storage.googleapis.com/index.html

使用requests爬取动态渲染的页面

import requests

header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
    'Accept-Encoding': 'gzip',
    'Connection': 'keep-alive',
    'Host': 'www.aqistudy.cn',
}

url = "https://192.168.1.1/aqistudy/monthdata.php?city=北京"
res = requests.get(url, headers=header)
if res.status_code == 200:
    print("请求成功")
    with open("aqistudy.txt", encoding="utf8", mode='w+') as f:
        f.write(res.text)
else:
    print("请求失败")
  <table width="100%" class="table table-condensed table-bordered table-striped table-hover table-responsive" style="margin-bottom:5px">
      <tr height=38px>
        <th style="background:#d9edf7">月份</th>
        <th style="background:#d9edf7">AQI</th>
        <th style="background:#d9edf7" class="hidden-xs">范围</th>
        <th style="background:#d9edf7" width="80px" >质量等级</th>
        <th style="background:#d9edf7">PM2.5</th>
        <th style="background:#d9edf7">PM10</th>
        <th style="background:#d9edf7" class="hidden-xs">SO2</th>
        <th style="background:#d9edf7" class="hidden-xs">CO</th>
        <th style="background:#d9edf7" class="hidden-xs">NO2</th>
        <th style="background:#d9edf7" class="hidden-xs">O3</th>
      </tr>
  </table>
var muwSVVIti = 'GETMONTHDATA';
  endebug(false, function () {
    document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!');
    document.write("<br/>");
    document.write("Welcome for People, Not Welcome for Machine!");
    diwAafiP = true;
  });

  txsdefwsw();
  document.onkeydown = function() {
    if ((e.ctrlKey) && (e.keyCode == 83)) {
      alert("检测到非法调试,CTRL + S被管理员禁用");
      return false;
    }
  }

  document.onkeydown = function() {
    var e = window.event || arguments[0];
    if (e.keyCode == 123) {
      alert("检测到非法调试,F12被管理员禁用");
      return false;
    }
  }

  document.oncontextmenu = function() {
    alert('检测到非法调试,右键被管理员禁用');
    return false;
  }
}

可以看到 返回的页面源码中 只有一个天气表格的框架, 没有我们需要的天气信息. 而且出现了被检测的信息. 出现这种情况 是因为:

● 目标网页是动态渲染的页面, 所以我们只能看到天气表格的框架,看不到具体的信息

● 目标网页检测到selenium 禁止调试

Scrapy + Selenium

  1. 运行一个Scrapy的项目
import scrapy

class ApistudyMainSpider(scrapy.Spider):
    name = 'apistudy_main'
    allowed_domains = ['192.168.1.1']

    def start_requests(self):
        start_url = r'https://192.168.1.1/aqistudy/monthdata.php?city=北京'
        yield scrapy.Request(url=start_url, callback=self.parse, dont_filter=True)

    def parse(self, response):
        yield {'text': response.text}
class AqistudyPipeline(object):
    # def process_item(self, item, spider):
    #     return item

    def open_spider(self, spider):
        self.file = open('my.html', 'w', encoding='utf-8')

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        self.file.write(str(item['text']))
class RandomHeaderMiddleWare:
    def __init__(self):
        self.user_agents = USER_AGENTS

    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)
        # 如果IP被限制, 可以在此下载中间件添加代理
        option = webdriver.ChromeOptions()
        option.add_argument('--headless')       # 无界面运行
        option.add_argument('--disable-gpu')    # 禁止gpu加速
        option.add_argument("no-sandbox")       # 取消沙盒模式
        option.add_argument("disable-blink-features=AutomationControlled")  # 禁用启用Blink运行时的功能
        option.add_experimental_option('excludeSwitches', ['enable-automation'])    # 开发者模式
        driver = webdriver.Chrome(options=option)
        # 移除 `window.navigator.webdriver`. scrapy 默认为True
        driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                Object.defineProperty(navigator, 'webdriver', {
                  get: () => undefined
                })
              """
        })
        driver.get(request.url)
        driver.implicitly_wait(5)
        content = driver.page_source
        driver.quit()
        return HtmlResponse(request.url, encoding="utf-8", body=content, request=request)

    def process_response(self, request, response, spider):
        return response

    def process_exception(self, request, exception, spider):
        return None
SPIDER_MIDDLEWARES = {
    'aqistudy.middlewares.AqistudySpiderMiddleware': 543,
}

DOWNLOADER_MIDDLEWARES = {
    'aqistudy.middlewares.AqistudyDownloaderMiddleware': 543,
    'aqistudy.middlewares.RandomHeaderMiddleWare': 545,     # 添加下载中间件
}

ROBOTSTXT_OBEY = False
 <table width="100%" class="table table-condensed table-bordered table-striped table-hover table-responsive"
                   style="margin-bottom:5px">
                <tbody>
                <tr height="38px">
                    <th style="background:#d9edf7">月份</th>
                    <th style="background:#d9edf7">AQI</th>
                    <th style="background:#d9edf7" class="hidden-xs">范围</th>
                    <th style="background:#d9edf7" width="80px">质量等级</th>
                    <th style="background:#d9edf7">PM2.5</th>
                    <th style="background:#d9edf7">PM10</th>
                    <th style="background:#d9edf7" class="hidden-xs">SO2</th>
                    <th style="background:#d9edf7" class="hidden-xs">CO</th>
                    <th style="background:#d9edf7" class="hidden-xs">NO2</th>
                    <th style="background:#d9edf7" class="hidden-xs">O3</th>
                </tr>
                <tr>
                    <td align="center"><a href="daydata.php?city=北京&month=2013-12">2013-12</a></td>
                    <td align="center">100</td>
                    <td align="center" class="hidden-xs">23~291</td>
                    <td align="center"><span
                            style="display:block;width:60px;text-align:center;background-color:#efdc31;color:black;">轻度污染</span>
                    </td>
                    <td align="center">73</td>
                    <td align="center">97</td>
                    <td align="center" class="hidden-xs">37</td>
                    <td align="center" class="hidden-xs">1.73</td>
                    <td align="center" class="hidden-xs">56</td>
                    <td align="center" class="hidden-xs">38</td>
                </tr>
                <tr>...>
                </tbody>
            </table>

总结

在撰写爬虫程序时, 遇到动态渲染的页面我们可以使用Scrapy+Selenium对页面规避反爬策略和爬取页面信息. 虽然webdriver影响到了Scrapy 的运行速度, 我们还可以使用scrapy-redis让我们的爬虫变成分布式以提高效率。

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • 使用 Scrapy + Selenium 爬取动态渲染的页面

    在通过scrapy框架进行某些网站数据爬取的时候,往往会碰到页面动态数据加载的情况发生,如果直接使用scrapy对其url发请求,是绝对获取不到那部分动态加载出...

    Tester_muller
  • Scrapy+Selenium爬取动态渲染网站

    在通过scrapy框架进行某些网站数据爬取的时候,往往会碰到页面动态数据加载的情况发生,如果直接使用scrapy对其url发请求,是绝对获取不到那部分动态加载出...

    py3study
  • scrapy结合selenium进行动态加载页面内容爬取

    使用requests进行数据获取的时候一般使用的是respond.text来获取网页源码,然后通过正则表达式提取出需要的内容。

    zx钟
  • 爬虫系列(16)Scrapy 框架-爬取JS生成的动态页面。

    有的页面的很多部分都是用JS生成的,而对于用scrapy爬虫来说就是一个很大的问题,因为scrapy没有JS engine,所以爬取的都是静态页面,对于JS生成...

    野原测试开发
  • 【5分钟玩转Lighthouse】爬取JavaScript动态渲染页面

    这些日子写过不少爬虫,想说些自己对于爬虫的理解,与本文无关,仅想学爬取JavaScript页面的同学可跳过。

    AmazzzingShang
  • [Python爬虫]使用Python爬取动态网页-腾讯动漫(Selenium)

    好久没更新Python相关的内容了,这个专题主要说的是Python在爬虫方面的应用,包括爬取和处理部分

    bsbforever
  • 使用Python爬取动态网页-腾讯动漫(Selenium)

    然后使MySQL停止更新非唯一索引(ALTER TABLE DISABLE KEYS),这样能加快插入速度

    bsbforever
  • Scrapy框架的使用之Scrapy爬取新浪微博

    崔庆才
  • Python反爬研究总结

    每次请求设置随机user-agent头。可以引入fake_useragent模块或从http://useragentstring.com/pages/usera...

    Lansonli
  • Scrapy框架的使用之Scrapy对接Splash

    崔庆才
  • 13、web爬虫讲解2—Scrapy框架爬虫—Scrapy爬取百度新闻,爬取Ajax动态生成的信息

    crapy爬取百度新闻,爬取Ajax动态生成的信息,抓取百度新闻首页的新闻rul地址

    天降攻城狮
  • Scrapy-Splash:学完秒变爬虫大佬

    开发爬虫的时候,因为网页中有数据动态加载(可参考之前文章)的部分,很多数据是后面渲染上的。爬虫程序只能爬取渲染前的数据,所以很多我们在网站上看到的数据,爬虫并不...

    叫我阿柒啊
  • 爬虫课堂(二十三)|使用Splash爬取动态页面(1)

    黄小怪
  • 干货|普通反爬虫机制的应对策略

    爬虫与反爬虫,这相爱相杀的一对,简直可以写出一部壮观的斗争史。而在大数据时代,数据就是金钱,很多企业都为自己的网站运用了反爬虫机制,防止网页上的数据被爬虫爬走。...

    灯塔大数据
  • Python网络爬虫精要

    requests负责向网页发送HTTP请求并得到响应,parsel负责解析响应字符串,selenium负责JavaScript的渲染。

    全栈程序员站长
  • 10个Python爬虫框架推荐,你使用的是哪个呢?

    实现爬虫技术的编程环境有很多种,Java、Python、C++等都可以用来爬虫。但很多人选择Python来写爬虫,为什么呢?因为Python确实很适合做爬虫,丰...

    程序员皮克
  • Scrapy 对接 Splash

    本节我们来了解下 Scrapy 对接 Splash 来进行页面抓取的方式,希望对大家有所帮助。

    崔庆才

扫码关注腾讯云开发者

领取腾讯云代金券