Logo

郎哥编程

爬取动态页面

2020-12-22 259

前面的爬虫案例爬取的网站都是静态页面内容,即内容都是预先生成的。但很多网站页面的内容都是动态生成的,即由浏览器运行页面中的JS脚本动态生成。由JS脚本生成的动态内容,前面的爬虫案例程序会爬取失败。

1、 爬取失败的案例

先来看一个简单爬取动态页面的案例,在Google Chrome浏览器中打开下面的页面:

http://milihua.com/sample/dynamic.aspx

页面会显示两张图片,按下F12键,浏览器会打开网页代码调试窗口,在代码调试窗口,单击Sources选项卡,会看到网页的源代码,如下图所示:

45.png

从代码中可以看出,在id值为content元素下面有<ul><li><img>标签,<img>标签的src属性指向图片的URL链接。

若在浏览器中查看网页源码,会看到下面的源代码:

46.png

从浏览器看到的源代码,在id值为content元素下面没有任何内容,那么这些内容是怎样产生的呢?

下面的JS代码可以让我们了解页面动态内容的创建细节:

<script type="text/javascript">
  $(document).ready(function () {
   gecontent();
 })
 function gecontent() {
   $.getJSON("/Handle/getcontent.ashx", function (json) {
     if (json == null || json == 'undefined') {
        return;
      }
     var content = "";
      content += "<ul>";
     content += "<li>"
     content += "<img src=" + json.imgurl1 + ">";
     content += "</li>";
     content += "<li>"
     content += "<img src=" + json.imgurl2 + ">";
     content += "</li>";
     content += "</ul>";
      $("#content").append(content);
  }, "json");
 
}
</script>

JS函数gecontent()在网页元素加载完成后被执行,函数使用Ajax技术向服务端发送getcontent.ashx请求,getcontent.ashx返回JSON数据,函数拼接HTML字符串和JSON数据,最后将拼接完成的HTML字符串添加到id值为content的元素内。

下面使用scrapy shell环境来爬取dynamic.aspx网页,打开Windows命令窗口,在命令行窗口输入下面的命令:

scrapy shell http://milihua.com/sample/dynamic.aspx

爬取成功后,在shell环境下输入下面的命令(输出爬取的内容):

print(response.text)

从输出的爬取内容可以看出,id值为content的元素下面没有任何内容,爬虫爬取失败。

爬取此类动态网页,需要先执行网页内的JavaScript代码渲染页面,然后再爬取,这就用到了JavaScript渲染引擎Selenium。

2、 使用Selenium执行网页脚本

Selenium是用于WEB应用程序的测试工具,它可以在浏览器中启动WEB程序(网页也属于WEB应用程序),并执行网页中的脚本内容,返回执行结果。

要让Scrapy爬虫使用Selenium执行网页脚本,需要对原来的爬虫流程进行修改,原来的爬取过程如下图所示:

50.png

Scrapy引擎将爬取的Request请求提交给下载管理器,下载器管理器下载网页数据,将网页数据封装到Response内并返回Response,Scrapy引擎会回调在Request对象设置的回调函数,并传入Responses对象。若Request对象没有设置回调函数,将会调用Spider的parse()方法。

若Request请求的页面是动态页面,Responses对象存储的页面内容没有动态加载的数据,要想获取页面动态加载的数据,则需要在下载管理器内部调用Selenium执行网页脚本,将Selenium返回的内容封装到response,然后将response对象返回给Scrapy引擎。

51.png

3、 下载中间件

中间件(Middleware)是Scrapy框架的一个核心概念,使用中间件可以钩住Scrapy请求和响应处理,对请求和响应的数据进行定制化修改,从而开发出适应不同情况的爬虫。

下载中间件的作用是钩住(拦截)请求和响应,在请求中下载中间件可以修改求头信息(User-Agent)、设置相关请求对象的代理ip等内容,在响应中下载中间件可以对响应进行一系列处理,例如执行动态网页的脚本,封装Response并传递给引擎。

Scrapy允许开发者可以使用多个下载中间件钩住Scrapy的请求和响应处理。一个下载中间件是一个Python类,它定义了一个或多个方法,用来处理请求和响应。

53.png

注释(1)

Scrapy引擎调用下载管理器发送request请求时,该方法被调用。开发者可以在该方法内修改Request请求对象,并返回新的request请求对象。

若该方法返回None,Scrapy将继续处理这个请求,调用后续的中间件来处理请求。若该方法返回Reponse响应对象,Scrapy会停止调用后续中间件的process_request()方法,但会调用后续中间件的process_response()方法。

参数request是请求对象。参数spider是发送该请求的爬虫实例对象。

注释(2)

网页下载完成后,该方法被调用。开发者可以在该方法内修改Reponse响应对象,并返回新的Reponse响应对象。

若该方法返回Reponse响应对象,Scrapy将继续处理这个响应对象,调用后续的中间件来处理响应对象。若该方法返回Request请求对象,Scrapy会暂停中间件链的调用,并将返回的请求对象加入请求队列。

参数request是请求对象。参数response是待处理的响应对象。参数spider是待处理响应对象的爬虫实例对象。

如何使用下载中间件?

Scrapy框架在创建爬虫程序时,已经生成了一个默认的中间件Python类文件,文件名称是middlewares.py。文件定义了两个Python类:一个Python类是爬虫中间件类;一个Python类是下载中间件类。这两个类都给出了默认方法,开发者可以修改这些默认方法的代码,以定制化自己的需求。

要启用下载中间件,需要修改settings配置文件,去掉下载中间件的注释:

DOWNLOADER_MIDDLEWARES = {

    'matplotlib.middlewares.MatplotlibDownloaderMiddleware': 543,

}

DOWNLOADER_MIDDLEWARES是下载中间件的配置项,配置项内容是matplotlib爬虫项目的内容。

4、 安装Selenium和Chorme浏览器

安装Selenium

在Python环境下安装Selenium非常简单,在Windows命令行窗口输入命令:

pip3 install selenium

pip3会自动下载Selenium并安装。

安装Chorme浏览器

Chorme浏览器是谷歌浏览器,在搜索引擎搜索Chorme浏览器可以找到Chorme浏览器的下载地址,建议下载谷歌官方发布的Chorme浏览器。

使用Selenium驱动chrome浏览器需要下载chromedriver,chromedriver是Chorme浏览器的驱动程序。chromedriver版本需要与Chromee的版本对应,若版本不一致,会导致Selenium启动Chorme浏览器失败。

查看Chorme浏览器的版本,可以通过Chorme浏览器“帮助”菜单的子菜单项“关于Google Chrome(G)”查看。下图是Chorme浏览器的一个版本:

 55.png

图中Chorme浏览器的版本号是76.0.3809.87,下载chromedriver驱动时,也需要选择与Chorme浏览器版本一致或接近的chromedriver版本下载。

下载chromedriver驱动可通过下面的网址下载:

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

5、 建立爬取动态网页爬虫项目

启动PyCharm建立项目,项目名称为dynamic。在项目终端窗口使用Scrapy创建项目命令创建爬虫项目,在终端窗口输入下面的Scrapy命令并回车执行命令:

scrapy startproject dynamic

在终端窗口将当前工作目录切换到dynamic爬虫项目目录,输入创建爬虫命令:

scrapy genspider spider_dynamic www.milihua.com

命令执行成功后,Scrapy会在项目的spiders目录创建爬虫spider_ dynamic.py模块文件。

(1)编辑items.py文件,存储dynamic.aspx网页内图片路径。

import scrapy
class DynamicItem(scrapy.Item):
    # 图片链接
    img_url = scrapy.Field()

(2)编辑middlewares.py中间件文件,该中间件文件是Scrapy默认生成的,文件内定义了DynamicSpiderMiddleware爬虫中间件类和下载中间件DynamicDownloaderMiddleware类,当前主要编辑下载中间件类的process_request()方法。

# 修改process_request()方法
    def process_request(self, request, spider):
        # 获取Chrome浏览器实例对象
        bro = spider.bro
        # 若request.url在请求列表中
        if request.url in spider.start_urls:
            # 调用Chrome实例对象get方法向浏览器发送url请求
            bro.get(request.url)
            # 休眠5秒,等待Chrome浏览器加载request.url
            time.sleep(5)
            # page_source是Chrome实例对象执行URL后的网页代码
            html = bro.page_source
            # 关闭Chrome浏览器
            bro.quit()
            # 返回Response对象,通知Scrapy引擎停止调用后续调用后续中间件的process_request()方法
            return scrapy.http.HtmlResponse(url=request.url, body=html.encode('utf-8'), encoding='utf-8',
                                            request=request)
        return None

下载中间件类重写了process_request()方法,该方法传入request和spider参数,request是发送的请求,spider参数是爬虫类的实例对象。

在方法内部调用Chrome浏览器对象的get()方法,执行request对象内的url,并调用time模块的sleep()方法让程序进入休眠,等待Chrome浏览器对象执行url任务的完成。page_source是Chrome实例对象的属性,该属性存储了Chrome实例对象执行URL后的网页代码。最后构建一个新的HtmlResponse实例对象并返回。

(3)编辑spider_dynamic爬虫文件。

import scrapy
from selenium import webdriver
# 导入scrapy选择器
from scrapy.selector import Selector
# 导入scrapy选择器
from dynamic.items import DynamicItem
class SpiderDynamicSpider(scrapy.Spider):
    name = 'spider_dynamic'
    allowed_domains = ['www.milihua.com']
    # 爬取动态网页
    start_urls = ['http://milihua.com/sample/dynamic.aspx']
    # 重写init方法
    # 实例Chrome对象,启动本地安装的Chrome浏览器
    # Chrome实例对象名称为bro,下载中间件会使用到该对象
    def __init__(self):
        # 获取Chrome浏览器启动选项
        options = webdriver.ChromeOptions()
        # 配置Chrome以管理员权限运行
        options.add_argument('--no-sandbox')
        # 配置Chrome以无界面方式运行
        options.add_argument('--headless')
        # 以配置的启动选项实例化Chrome对象
        self.bro = webdriver.Chrome(chrome_options=options)
    def parse(self, response):
        # 使用XPath获取img节点
        item_nodes = response.xpath("//img").extract()
        # 遍历节点
        for item_node in item_nodes:
            item = DynamicItem()
            # 使用xpath表达式获取节点的src属性值
            item["img_url"] = Selector(text=str(item_node)).xpath('//@src').extract()
            # 使用yield语句返回item给parse的调用者
            yield item

爬虫类SpiderDynamicSpider重写了__init__()方法,该方法在爬虫实例化时被调用,在__init__()方法内实例化Chrome浏览器对象,并赋值给类属性bro,该属性在下载中间件被使用。

(4)要启用下载中间件,需要修改settings配置文件,去掉下载中间件的注释:

DOWNLOADER_MIDDLEWARES = {
    'dynamic.middlewares.DynamicDownloaderMiddleware': 543,
}

(5)运行爬虫

在PyCharm环境中,打开终端窗口,在终端窗口将当前工作目录切换到爬虫项目目录,输入Scrapy运行爬虫命令:

scrapy crawl spider_dynamic -o items.json

爬虫运行完成后,会将下载的图片文件存储到爬虫项目目录下的images目录。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论