逆向爬虫20 Scrapy-Splash入门

逆向爬虫20 Scrapy-Splash入门

一. Splash

在学习Splash之前,先要明白为什么要学它,它能帮我们完成什么工作,什么情况下适合使用Splash?

splash是一个可以动态渲染js的工具. 有助于我们完成复杂的js内容加载工作. 你可以理解为另一个没有界面的selenium。

由于Selenium经常被用于爬虫,越来越多的网站开始针对Selenium做反爬技术,因此Splash算是Selenium的一个替代品,但它又不能完全替代Selenium,Splash无法处理登录验证,人机校验等反爬手段。

Splash的速度比Selenium还慢,它的访问过程有点类似与代理IP,如果还要加上代理IP的话,就更慢了。

那为什么还要学Splash呢,一是它可以作为Selenium的备用方案,二是Splash可以将浏览器抓包的全过程输出为字典,这是Selenium做不到的,拿到抓包全过程,可以方便我们后续优化爬虫性能。

1.1 splash安装

splash的安装过程十分复杂. 复杂到官方都不推荐你去手动安装它.

官方建议. 用docker去安装splash. 所以. 你需要先去安装docker. 但是docker这玩意在windows上支持非常不好. 各种各样的问题. 外加上后期我们要把爬虫部署到linux. 那干脆. 我们就安装一个linux. 在linux上搞docker是非常easy的.

有能力, 不怕苦的同学可以在windows上搞一个docker试试. 我这里就不带你们找坑踩了. 直接上Linux.

1.1.1安装VM

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.1.2 安装Linux

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

安装好的linux后,我们需要学会使用linux的一个工具. 叫yum, 我们需要用它来帮我们完成各种软件的安装. 十分的方便. 我们先用ifconfig来做一个测试.

yum search ifconfig   // 搜索出ifconfig的包
yum install net-tools.x86_64  // 安装该软件, 安装过程中会出现很多个询问. 直接y即可

发现了吧, 在linux这个破黑窗口里. 属实难受+憋屈. 所以, 我们这里选择用ssh远程连接linux.

mac版本: 打开终端. 输入

ssh root@服务器ip地址
输入密码

就可以顺利的链接到你的linux服务器. 接下来. 我们可以使用各种命令来操纵linux了.

Windows:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.1.3 安装docker

​ 安装docker就一条例命令就好了

[root@sylar-centos-2 ~]# yum install docker

​ 配置docker的源,这里要使用国内的源,否则会慢死

[root@sylar-centos-2 ~]# vi /etc/docker/daemon.json
# 写入一下内容, 注意.先按'i', 更换为输入模式. 然后再填写内容
{
	"registry-mirrors": ["https://9cpn8tt6.mirror.aliyuncs.com"]
}
# 保存: 先按esc. 退出输入模式, 然后输入":wq" 表示写入, 退出. 就完事儿了
[root@sylar-centos-2 ~]# systemctl start docker    # 启动docker
[root@sylar-centos-2 ~]# docker ps      # 查看docker运行状态

如需关闭或者重新启动docker:

systemctl stop docker   # 停止docker服务
systemctl restart docker  # 重启docker服务

Vm -> cenos -> ssh -> docker -> splash

1.1.4 安装splash
  1. 拉取splash镜像

    docker pull scrapinghub/splash
    

    splash比较大. 大概2个G左右. 有点儿耐心等会儿就好了

  2. 运行splash

    docker run -p 8050:8050 scrapinghub/splash
    
  3. 打开浏览器访问splash

    http://192.168.31.82:8050/
    在这里插入图片描述

1.2 splash简单使用

​ 我们可以在文本框内输入百度的网址. 然后点击render. 可以看到splash会对我们的网页进行动态的加载. 并返回截图. 运行状况. 以及页面代码(经过js渲染后的)
在这里插入图片描述
在这里插入图片描述

快速解释一下, script中的脚本. 这里面用的是lua的脚本语法. 所以看起来会有些难受.

function main(splash, args)  -- 主函数
  assert(splash:go(args.url))  -- 进入xxx页面
  assert(splash:wait(0.5))   -- 等待0.5秒
  return {  -- 返回
    html = splash:html(),  -- splash:html() 页面源代码
    png = splash:png(),   -- splash:png() 页面截图
    har = splash:har(),   -- splash:har() 页面加载过程
  }
end   -- 函数结束

有必要说明一下. 在lua中, .表示的是属性(变量), :表示的是方法(函数)的调用.

常见操作符都一样. 剩下的. 我们到案例里看.

1.3 splash的http-api接口

splash提供了对外的http-api接口. 我们可以像访问一个普通url一样访问splash. 并由splash帮助我们渲染好页面内容.

http://192.168.31.82:8050/render.html?url=http://www.baidu.com

虽然看不出任何差别. 但是你心里要清楚一个事情. 此时拿到的直接是经过js渲染后的html

我们换个url你就知道了

http://192.168.31.82:8050/render.html?url=https://www.endata.com.cn/BoxOffice/BO/Year/index.html&wait=5

endata这个网站. 它的数据是后期经过ajax请求二次加载进来的. 我们通过splash可以等待它后期加载完再拿html.

综上, splash的工作机制:

在这里插入图片描述

整个一个代理服务器的逻辑. ~~~~

二. python中使用splash

"""
    # splash提供的api接口
    渲染html的接口
    http://192.168.63.128:8050/render.html?url=你的url&wait=等待时间&time_out=超时时间

    截图的接口
    http://192.168.63.128:8050/render.png  参数和render.html基本一致, 可选width, height

    加载过程接口
    http://192.168.63.128:8050/render.har  参数和render.html基本一致

    json接口
    http://192.168.63.128:8050/render.json  参数和render.html基本一致

    执行lua脚本的接口
    http://192.168.31.184:8050/execute?lua_source=你要执行的lua脚本
"""

2.1 调用render接口

很简单,发送一个requests.get请求,把get的参数放进params中

import requests

resp = requests.get(
    url='http://192.168.63.128:8050/render.html',
    params={
        "url":'https://www.endata.com.cn/BoxOffice/BO/Year/index.html',
        "wait": 5
    }
)
with open("log.html", mode="w", encoding="utf-8") as f:
    f.write(resp.text)

2.2 调用execute接口

调用execute接口就可以执行相对比较复杂的浏览器操作了,这里用 https://news.163.com/ 来说明怎么模拟浏览器操作,利用splash来抓取ajax动态加载来的数据。

下图是网易新闻主页拉到最下面的样子,有一个 “加载更多” 按钮。

在这里插入图片描述

点击该按钮后,URL没有改变,但显示了更多的新闻,这就是ajax动态加载来的数据。

在这里插入图片描述

再点击一次 “加载更多” 按钮,拉到最下面发现展示完了。
在这里插入图片描述

要用execute接口实现这个功能,就必须编写Lua脚本和Javascript脚本,这里的代码本身的功能并不复杂,但是设计到多门语言混合编程,因此比较唬人。
在这里插入图片描述

import requests
from lxml import etree

lua_source = """
function main(splash, args)
  assert(splash:go("https://news.163.com/"))
  assert(splash:wait(2))
  -- 准备一个js函数. 预加载
  -- jsfunc是splash预留的专门为了js代码和lua代码结合准备的
  get_btn_display = splash:jsfunc([[
        function(){
            return document.getElementsByClassName('load_more_btn')[0].style.display;
        }
    ]])
  
  while(true)
  do
    splash:runjs("document.getElementsByClassName('load_more_btn')[0].scrollIntoView(true)")
    splash:select(".load_more_btn").click()
    splash:wait(1)
    -- 判断load_more_btn是否是none.
    display = get_btn_display()
    if(display == 'none')
      then
        break
      end
  end
  
  return splash:html()  -- 直接返回页面源代码
end
"""

resp = requests.get(
    url='http://192.168.63.128:8050/execute',
    params={
        "lua_source": lua_source
    }
)
with open("log2.html", mode="w", encoding="utf-8") as f:
    f.write(resp.text)

tree = etree.HTML(resp.text)
divs = tree.xpath('/html/body/div[1]/div[3]/div[2]/div[3]/div[2]/div[5]/div/ul/li[1]/div[2]/div')
for div in divs:
    a = div.xpath('./div/div/h3/a')
    if not a:  # 过滤掉广告
        continue
    a = a[0]
    print(a.xpath("./@href")[0])
    print(a.xpath("./text()")[0])

三. Scrapy_splash模块

实现和2.2一样的功能

开始动手

scrapy startproject news
cd news
scrapy genspider wangyi 163.com
wangyi.py文件

在这里插入图片描述

settings.py文件

在这里插入图片描述

wangyi.py源码
import scrapy
from scrapy_splash.request import SplashRequest

lua_source = """
function main(splash, args)
  assert(splash:go(args.url))
  assert(splash:wait(2))
  -- 准备一个js函数. 预加载
  -- jsfunc是splash预留的专门为了js代码和lua代码结合准备的
  get_btn_display = splash:jsfunc([[
    	function(){
    		return document.getElementsByClassName('load_more_btn')[0].style.display;
  		}
    ]])

  while(true)
  do
    splash:runjs("document.getElementsByClassName('load_more_btn')[0].scrollIntoView(true)")
    splash:select(".load_more_btn").click()
    splash:wait(1)
    -- 判断load_more_btn是否是none.
    display = get_btn_display()
    if(display == 'none')
      then
        break
      end
  end

  return splash:html()  -- 直接返回页面源代码
end
"""

class WangyiSpider(scrapy.Spider):
    name = 'wangyi'
    allowed_domains = ['163.com']
    start_urls = ['http://news.163.com/']
    # 重写start_requests
    def start_requests(self):
        yield SplashRequest(
            url=self.start_urls[0],
            callback=self.parse,
            endpoint='execute',     # 终端表示你要执行哪一个splash的服务
            args={
                "lua_source": lua_source
            }
        )

    def parse(self, resp):
        divs = resp.xpath('/html/body/div[1]/div[3]/div[2]/div[3]/div[2]/div[5]/div/ul/li[1]/div[2]/div')
        for div in divs:
            a = div.xpath('./div/div/h3/a')
            if not a:  # 过滤掉广告
                continue
            a = a[0]
            print(a.xpath("./@href").extract_first())
            print(a.xpath("./text()").extract_first())
settings.py源码
BOT_NAME = 'news'

SPIDER_MODULES = ['news.spiders']
NEWSPIDER_MODULE = 'news.spiders'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False
LOG_LEVEL = "WARNING"

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'

# scrapy_splash
# 渲染服务的url, 这里换成你自己的
SPLASH_URL = 'http://192.168.63.128:8050'
# 下载器中间件, 这个必须要配置
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

# 这个可由可无
# SPIDER_MIDDLEWARES = {
#     'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
# }
# 去重过滤器, 这个必须要配置
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
# 使用Splash的Http缓存, 这个必须要配置
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
<think>好的,我现在需要回答用户关于Scrapy框架能否爬取动态网站的问题。首先,用户之前已经询问过软科官网是否是动态网站,现在接着问Scrapy是否能处理动态网站。我需要结合之前的回答,提供准确的信息。 首先,我应该回忆一下Scrapy的基本功能。Scrapy是一个用Python编写的开源爬虫框架,主要用于高效地抓取网页并提取结构化数据。它基于异步处理,适合大规模数据采集。但Scrapy默认是处理静态页面的,也就是直接下载HTML内容进行解析。 然而,用户的问题是关于动态网站的。动态网站的内容通常通过JavaScript异步加载,比如通过AJAX或XHR请求获取数据,或者使用前端框架如React、Vue动态渲染内容。这种情况下,直接使用Scrapy可能无法获取到完整的页面内容,因为数据可能不在初始的HTML中,而是后续由JavaScript生成的。 所以,我需要明确告诉用户Scrapy本身是否能处理这种情况。答案应该是:Scrapy本身不直接支持执行JavaScript,因此对于完全依赖客户端渲染的动态内容,单独使用Scrapy可能无法爬取。不过,用户可以通过其他方法结合Scrapy来实现动态网站的爬取。 接下来,我需要分步骤说明如何让Scrapy处理动态网站。比如,结合Selenium或Splash这样的工具。Selenium可以驱动真实浏览器执行JavaScript,而Splash是一个轻量级的JavaScript渲染服务,由Scrapy的开发者维护,可能更易于集成。 然后,需要比较这些方法的优缺点。例如,使用中间件集成Selenium会增加爬取的复杂性和运行时间,而调用API接口则更高效,但需要先找到并分析API的结构。用户之前已经提到过直接调用API的方法,可能需要在这里再次强调这一点。 另外,用户可能关心具体的实现步骤,所以需要提供示例代码或配置方法。比如,如何在Scrapy项目中配置Splash,或者使用scrapy-selenium库的示例。 还需要提醒用户注意事项,比如遵守robots.txt,设置合理的下载延迟,处理可能的反爬机制等。同时,动态网站的数据可能以JSON格式返回,因此使用Scrapy的JsonItemExporter可能会更方便。 最后,总结Scrapy在动态网站爬取中的适用性,建议优先使用API方法,若不可行再考虑无头浏览器或渲染服务。确保回答结构清晰,帮助用户逐步解决问题。</think>--- ### **Scrapy框架能否爬取动态网站?** **直接回答**: Scrapy **默认不支持动态网站**(如依赖JavaScript渲染的内容),但通过结合其他工具(如Splash、Selenium或直接调用API接口),可以实现对动态网站的爬取。 --- ### **1. Scrapy的局限性** - **静态内容处理**:Scrapy的核心功能是高效下载和解析静态HTML页面(即服务器直接返回的HTML内容)。 - **动态内容问题**:若页面数据通过 **JavaScript/AJAX动态加载**(如点击翻页后URL不变、数据异步加载),Scrapy无法直接获取这些内容,因为动态数据不在原始HTML中。 --- ### **2. 动态网站的爬取方案** #### **方案 1:直接调用数据接口(推荐)** - **适用场景**:动态网站通过 **XHR/Fetch请求** 从后端API获取数据(例如返回JSON格式)。 - **实现步骤**: 1. 使用浏览器开发者工具(F12 → Network → XHR)找到数据接口。 2. 分析接口的请求参数(如页码、筛选条件)和请求头(如`Cookie`、`User-Agent`)。 3. 在Scrapy中发送模拟请求,直接获取结构化数据(如JSON)。 - **示例代码**: ```python import scrapy import json class UniversitySpider(scrapy.Spider): name = 'shanghairanking' start_urls = ['https://www.shanghairanking.cn/api/rankings'] def parse(self, response): data = json.loads(response.text) for item in data['data']: yield { 'name': item['universityName'], 'rank': item['ranking'] } ``` #### **方案 2:集成无头浏览器(如Splash或Selenium)** - **适用场景**:无法找到数据接口,且必须渲染JavaScript才能获取内容。 - **方法 1:使用Splash(轻量级渲染服务)** - Splash是一个专为Scrapy设计的JavaScript渲染工具,支持Lua脚本。 - **配置步骤**: 1. 安装Docker并运行Splash服务:`docker run -p 8050:8050 scrapinghub/splash` 2. 在Scrapy中安装`scrapy-splash`库:`pip install scrapy-splash` 3. 修改Scrapy的`settings.py`: ```python SPLASH_URL = 'http://localhost:8050' DOWNLOADER_MIDDLEWARES = { 'scrapy_splash.SplashCookiesMiddleware': 723, 'scrapy_splash.SplashMiddleware': 725, } ``` - **示例代码**: ```python class DynamicSpider(scrapy.Spider): name = 'dynamic_spider' def start_requests(self): yield scrapy.Request( url='https://www.shanghairanking.cn/rankings/bcur/2024', meta={'splash': {'args': {'wait': 2}}}, # 等待2秒渲染 ) def parse(self, response): # 解析渲染后的HTML universities = response.css('.university-name::text').getall() for name in universities: yield {'name': name} ``` - **方法 2:集成Selenium(重型但灵活)** - 通过中间件将Selenium嵌入Scrapy流程,适合复杂交互(如登录、点击按钮)。 - **示例代码**: ```python from scrapy.http import HtmlResponse from selenium import webdriver class SeleniumMiddleware: def process_request(self, request, spider): driver = webdriver.Chrome() driver.get(request.url) html = driver.page_source driver.quit() return HtmlResponse(url=request.url, body=html, encoding='utf-8', request=request) # 在settings.py中启用中间件 DOWNLOADER_MIDDLEWARES = { 'your_project.middlewares.SeleniumMiddleware': 543, } ``` --- ### **3. 动态爬取的注意事项** 1. **效率问题**: - 无头浏览器(如Selenium)会显著降低爬取速度,**优先选择直接调用API**。 2. **反爬机制**: - 动态网站通常有更严格的反爬措施(如IP封锁、请求频率限制),需设置`DOWNLOAD_DELAY`或使用代理池。 3. **数据解析复杂度**: - 渲染后的HTML可能包含嵌套的JSON或复杂DOM结构,建议使用`parsel`或`json`库解析。 --- ### **4. 总结** | 方法 | 适用场景 | 优点 | 缺点 | |---------------------|-----------------------------------|--------------------------|--------------------------| | 直接调用API | 网站提供清晰的数据接口 | 速度快、资源消耗低 | 需逆向分析接口参数 | | Splash | 简单JavaScript渲染 | 轻量、与Scrapy集成方便 | 不支持复杂交互 | | Selenium | 需要模拟点击、登录等操作 | 功能强大、灵活 | 速度慢、资源占用高 | **建议**: - 若目标网站有公开API,优先使用Scrapy直接调用接口。 - 若必须渲染JavaScript,推荐使用Splash;若需复杂交互(如登录、翻页),再选择Selenium。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值