Day064 爬虫(六)

二十七、Splash 的使用

1. Splash介绍

Splash是一个JavaScript渲染服务,是一个带有HTTP API的轻量级浏览器,同时它对接了Python中的Twisted和QT库。利用它,我们同样可以实现动态渲染页面的抓取,(类似于前面学的 Selenium的功能)

2. 安装

2.1 安装docker

Daoker 的下载地址:get.daocloud.io/

2.2 拉取镜像

        docker pull scrapinghub/splash
      

2.3 用docker运行scrapinghub/splash

        docker run -p 8050:8050 scrapinghub/splash
      

2.4 查看效果

我们在8050端口上运行了Splash服务,打开 192.168.99.100:8050/即可看到其Web页面

v2-627c30ba01d89313173fe94f7008c16d_b.jpg


3 Splash对象属性

上图中main()方法的第一个参数是splash,这个对象非常重要,它类似于Selenium中的WebDriver对象

3.1 images_enabled

设置图片是否加载,默认情况下是加载的。禁用该属性后,可以节省网络流量并提高网页加载速度
注意的是,禁用图片加载可能会影响JavaScript渲染。因为禁用图片之后,它的外层DOM节点的高度会受影响,进而影响DOM节点的位置
因此,如果JavaScript对图片节点有操作的话,其执行就会受到影响
        function main(splash, args)
  splash.images_enabled = false
  splash:go('https://www.baidu.com')
  return {html=splash:html()}
end
      

3.2 plugins_enabled

可以控制浏览器插件(如Flash插件)是否开启
默认情况下,此属性是false,表示不开启
        splash.plugins_enabled = true/false
      

3.3 scroll_position

控制页面上下或左右滚动
        splash.scroll_position = {x=100, y=200}
      

4. Splash对象的方法

4.1 go()

该方法用来请求某个链接,而且它可以模拟GET和POST请求,同时支持传入请求头、表单等数据
        ok, reason = splash:go{url, baseurl=nil, headers=nil, http_method="GET", body=nil, formdata=nil}
      
返回结果是结果ok和原因reason
如果ok为空,代表网页加载出现了错误,此时reason变量中包含了错误的原因

参数含义url请求的URLbaseurl可选参数,默认为空,表示资源加载相对路径headers可选参数,默认为空,表示请求头http_method可选参数,默认为GET,同时支持POSTbody可选参数,默认为空,发POST请求时的表单数据,使用的Content-type为application/jsonformdata可选参数,默认为空,POST的时候的表单数据,使用的Content-type为application/x-www-form-urlencoded

        splash:go{"http://www.sxt.cn", http_method="POST", body="name=17703181473"}
      

4.2 wait()

控制页面的等待时间
        splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}
      

参数含义time等待的秒数cancel_on_redirect可选参数,默认为false,表示如果发生了重定向就停止等待,并返回重定向结果cancel_on_error可选参数,默认为false,表示如果发生了加载错误,就停止等待

        function main(splash)
    splash:go("https://www.taobao.com")
    splash:wait(2)
    return {html=splash:html()}
end
      

4.3 jsfunc()

直接调用JavaScript定义的方法,但是所调用的方法需要用双中括号包围,这相当于实现了JavaScript方法到Lua脚本的转换
        function main(splash, args)
  splash:go("http://www.sxt.cn")
  local scroll_to = splash:jsfunc("window.scrollTo")
  scroll_to(0, 300)
  return {png=splash:png()}
end
      

4.4 evaljs()与 runjs()

  • evaljs() 以执行JavaScript代码并返回最后一条JavaScript语句的返回结果
  • runjs() 以执行JavaScript代码,它与evaljs()的功能类似,但是更偏向于执行某些动作或声明某些方法
        function main(splash, args)
  splash:go("https://www.baidu.com")
  splash:runjs("foo = function() { return 'sxt' }")
  local result = splash:evaljs("foo()")
  return result
end
      

4.5 html()

获取网页的源代码
        function main(splash, args)
  splash:go("https://www.bjsxt.com")
  return splash:html()
end
      

4.6 png()

获取PNG格式的网页截图
        function main(splash, args)
  splash:go("https://www.bjsxt.com")
  return splash:png()
end
      

4.7 har()

获取页面加载过程描述
        function main(splash, args)
  splash:go("https://www.bjsxt.com")
  return splash:har()
end
      

4.8 url()

获取当前正在访问的URL
        function main(splash, args)
  splash:go("https://www.bjsxt.com")
  return splash:url()
end
      

4.9 get_cookies()

获取当前页面的Cookies
        function main(splash, args)
  splash:go("https://www.bjsxt.com")
  return splash:get_cookies()
end
      

4.10 add_cookie()

当前页面添加Cookie
        cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}

function main(splash)
    splash:add_cookie{"sessionid", "123456abcdef", "/", domain="http://bjsxt.com"}
    splash:go("http://bjsxt.com/")
    return splash:html()
end
      

4.11 clear_cookies()

可以清除所有的Cookies
        function main(splash)
    splash:go("https://www.bjsxt.com/")
    splash:clear_cookies()
    return splash:get_cookies()
end
      

4.12 set_user_agent()

设置浏览器的User-Agent
        function main(splash)
  splash:set_user_agent('Splash')
  splash:go("http://httpbin.org/get")
  return splash:html()
end
      

4.13 set_custom_headers()

设置请求头
        function main(splash)
  splash:set_custom_headers({
     ["User-Agent"] = "Splash",
     ["Site"] = "Splash",
  })
  splash:go("http://httpbin.org/get")
  return splash:html()
end
      

4.14 select()

选中符合条件的第一个节点
如果有多个节点符合条件,则只会返回一个
其参数是CSS选择器
        function main(splash)
  splash:go("https://www.baidu.com/")
  input = splash:select("#kw")
  splash:wait(3)
  return splash:png()
end
      

4.15 send_text()

填写文本
        function main(splash)
  splash:go("https://www.baidu.com/")
  input = splash:select("#kw")
  input:send_text('Splash')
  splash:wait(3)
  return splash:png()
end
      

4.16 mouse_click()

模拟鼠标点击操作
        function main(splash)
  splash:go("https://www.baidu.com/")
  input = splash:select("#kw")
  input:send_text('Splash')
  submit = splash:select('#su')
  submit:mouse_click()
  splash:wait(3)
  return splash:png()
end
      

4.17 代理Ip

        function main(splash)
    splash:on_request(function(request)
        request:set_proxy{
            'host':'61.138.33.20',
            'port':808,
            'username':'uanme',
            'password':'passwrod'
        }
    
     end)
    
    -- 设置请求头
    splash:set_user_agent("Mozilla/5.0")

    splash:go("https://httpbin.org/get")
    return splash:html()
end
      

5 Splash与Python结合

5.1 render.html

此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称,例如 http://192.168.99.100:8050/render.html
        import requests
url = 'http://192.168.99.100:8050/render.html?url=https://www.bjsxt.com&wait=3'
response = requests.get(url)
print(response.text)
      

5.2 render.png

此接口可以获取网页截图
        import requests
 
url = 'http://192.168.99.100:8050/render.png?url=https://www.jd.com&wait=5&width=1000&height=700'
response = requests.get(url)
with open('taobao.png', 'wb') as f:
    f.write(response.content)
      

5.3 execute

最为强大的接口。前面说了很多Splash Lua脚本的操作,用此接口便可实现与Lua脚本的对接
        import requests
from urllib.parse import quote
 
lua = '''
function main(splash)
    return 'hello'
end
'''
 
url = 'http://192.168.99.100:8050/execute?lua_source=' + quote(lua)
response = requests.get(url)
print(response.text)
      

二十八、Scrapy 框架-爬取JS生成的动态页面

问题

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

官网 splash.readthedocs.io/e

解决方案

  • 利用第三方中间件来提供JS渲染服务: scrapy-splash 等
  • 利用webkit或者基于webkit库
Splash是一个Javascript渲染服务。它是一个实现了HTTP API的轻量级浏览器,Splash是用Python实现的,同时使用Twisted和QT。Twisted(QT)用来让服务具有异步处理能力,以发挥webkit的并发能力

安装

  1. pip安装scrapy-splash库
         pip install scrapy-splash
      
  1. scrapy-splash使用的是Splash HTTP API, 所以需要一个splash instance,一般采用docker运行splash,所以需要安装docker
  2. 安装docker, 安装好后运行docker
  3. 拉取镜像
         docker pull scrapinghub/splash
      
  1. 用docker运行scrapinghub/splash
        docker run -p 8050:8050 scrapinghub/splash
      
  1. 配置splash服务(以下操作全部在settings.py):
    1. 使用splash解析,要在配置文件中设置splash服务器地址:

SPLASH_URL = '192.168.99.100:8050/'

    1. 将splash middleware添加到DOWNLOADER_MIDDLEWARE中

DOWNLOADER_MIDDLEWARES = { 'scrapy_splash.SplashCookiesMiddleware': 723, 'scrapy_splash.SplashMiddleware': 725, 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810, }

    1. Enable SplashDeduplicateArgsMiddleware

SPIDER_MIDDLEWARES = { 'scrapy_splash.SplashDeduplicateArgsMiddleware': 100 }
这个中间件需要支持cache_args功能; 它允许通过不在磁盘请求队列中多次存储重复的Splash参数来节省磁盘空间。如果使用Splash 2.1+,则中间件也可以通过不将这些重复的参数多次发送到Splash服务器来节省网络流量

    1. 配置消息队列所使用的过滤类

DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

    1. 配置消息队列需要使用的类

HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

样例

        import scrapy
from scrapy_splash import SplashRequest


class DoubanSpider(scrapy.Spider):
    name = 'douban'

    allowed_domains = ['douban.com']


def start_requests(self):
    yield SplashRequest('https://movie.douban.com/typerank?type_name=剧情&type=11&interval_id=100:90', args={'wait': 0.5})


def parse(self, response):
    print(response.text)
      


二十九、Docker Toolbox 安装

暂无笔记

三十、 Scrapy 框架-分布式

1. 介绍scrapy-redis框架

scrapy-redis

一个三方的基于redis的分布式爬虫框架,配合scrapy使用,让爬虫具有了分布式爬取的功能。

github地址: github.com/darkrho/scra

2. 分布式原理

 scrapy-redis实现分布式,其实从原理上来说很简单,这里为描述方便,我们把自己的核心服务器称为master,而把用于跑爬虫程序的机器称为slave

我们知道,采用scrapy框架抓取网页,我们需要首先给定它一些start_urls,爬虫首先访问start_urls里面的url,再根据我们的具体逻辑,对里面的元素、或者是其他的二级、三级页面进行抓取。而要实现分布式,我们只需要在这个starts_urls里面做文章就行了

我们在master上搭建一个redis数据库`(注意这个数据库只用作url的存储),并对每一个需要爬取的网站类型,都开辟一个单独的列表字段。通过设置slave上scrapy-redis获取url的地址为master地址。这样的结果就是,尽管有多个slave,然而大家获取url的地方只有一个,那就是服务器master上的redis数据库

并且,由于scrapy-redis自身的队列机制,slave获取的链接不会相互冲突。这样各个slave在完成抓取任务之后,再把获取的结果汇总到服务器上

好处

程序移植性强,只要处理好路径问题,把slave上的程序移植到另一台机器上运行,基本上就是复制粘贴的事情

3.分布式爬虫的实现

  1. 使用三台机器,一台是win10,两台是centos6,分别在两台机器上部署scrapy来进行分布式抓取一个网站
  2. win10的ip地址为192.168.31.245,用来作为redis的master端,centos的机器作为slave
  3. master的爬虫运行时会把提取到的url封装成request放到redis中的数据库:“dmoz:requests”,并且从该数据库中提取request后下载网页,再把网页的内容存放到redis的另一个数据库中“dmoz:items”
  4. slave从master的redis中取出待抓取的request,下载完网页之后就把网页的内容发送回master的redis
  5. 重复上面的3和4,直到master的redis中的“dmoz:requests”数据库为空,再把master的redis中的“dmoz:items”数据库写入到mongodb中
  6. master里的reids还有一个数据“dmoz:dupefilter”是用来存储抓取过的url的指纹(使用哈希函数将url运算后的结果),是防止重复抓取的

4. scrapy-redis框架的安装

        pip install scrapy-redis
      

5. 部署scrapy-redis

5.1 slave端

在windows上的settings.py文件的最后增加如下一行
        REDIS_HOST = 'localhost' #master IP

REDIS_PORT = 6379
      

配置好了远程的redis地址后启动两个爬虫(启动爬虫没有顺序限制)

6 给爬虫增加配置信息

        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"

ITEM_PIPELINES = {
    'example.pipelines.ExamplePipeline': 300,
    'scrapy_redis.pipelines.RedisPipeline': 400,
}
      

7 运行程序

7.1 运行slave

        scrapy runspider 文件名.py
      

开起没有先后顺序

7.2 运行master

        lpush (redis_key)  url #括号不用写
      

说明

  • 这个命令是在redis-cli中运行
  • redis_key 是 spider.py文件中的redis_key的值
  • url 开始爬取地址,不加双引号

8 数据导入到mongodb中

等到爬虫结束后,如果要把数据存储到mongodb中,就应该修改master端process_items.py文件,如下

        import redis

import pymongo

def main():

    r = redis.Redis(host='192.168.31.245',port=6379,db=0)

    client = pymongo.MongoClient(host='localhost', port=27017)

    db = client.dmoz

    sheet = db.sheet

    while True:


        source, data = r.blpop(["dmoz:items"])

        item = json.loads(data)

        sheet.insert(item)

if __name__ == '__main__':

    main()
      

9 数据导入到MySQL中

等到爬虫结束后,如果要把数据存储到mongodb中,就应该修改master端process_items.py文件,如下

        import redis
import pymysql
import json
def process_item():
    r_client = redis.Redis(host="127.0.0.1",port=6379,db =0)
    m_client = pymysql.connect(host="127.0.0.1",port=3306,user="root",passowrd="123456",db="lianjia")
    source,data =r_client.blpop("lianjia:item")
    item = json.loads(data)

    cursor = m_client.cursor()
    values = []
    cursor.execute(sql,values)
      



三十一、Scrapy-Redis 其他

setting文件配置

        #启用Redis调度存储请求队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
 
#确保所有的爬虫通过Redis去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
 
#默认请求序列化使用的是pickle 但是我们可以更改为其他类似的。PS:这玩意儿2.X的可以用。3.X的不能用
#SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"
 
#不清除Redis队列、这样可以暂停/恢复 爬取
#SCHEDULER_PERSIST = True
 
#使用优先级调度请求队列 (默认使用)
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
#可选用的其它队列
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
 
#最大空闲时间防止分布式爬虫因为等待而关闭
#这只有当上面设置的队列类是SpiderQueue或SpiderStack时才有效
#并且当您的蜘蛛首次启动时,也可能会阻止同一时间启动(由于队列为空)
#SCHEDULER_IDLE_BEFORE_CLOSE = 10
 
#将清除的项目在redis进行处理
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300
}
 
#序列化项目管道作为redis Key存储
#REDIS_ITEMS_KEY = '%(spider)s:items'
 
#默认使用ScrapyJSONEncoder进行项目序列化
#You can use any importable path to a callable object.
#REDIS_ITEMS_SERIALIZER = 'json.dumps'
 
#指定连接到redis时使用的端口和地址(可选)
#REDIS_HOST = 'localhost'
#REDIS_PORT = 6379
 
#指定用于连接redis的URL(可选)
#如果设置此项,则此项优先级高于设置的REDIS_HOST 和 REDIS_PORT
#REDIS_URL = 'redis://user:pass@hostname:9001'
 
#自定义的redis参数(连接超时之类的)
#REDIS_PARAMS  = {}
 
#自定义redis客户端类
#REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'
 
#如果为True,则使用redis的'spop'进行操作。
#如果需要避免起始网址列表出现重复,这个选项非常有用。开启此选项urls必须通过sadd添加,否则会出现类型错误。
#REDIS_START_URLS_AS_SET = False
 
#RedisSpider和RedisCrawlSpider默认 start_usls 键
#REDIS_START_URLS_KEY = '%(name)s:start_urls'
 
#设置redis使用utf-8之外的编码
#REDIS_ENCODING = 'latin1'
      


三十二、Scrapyd的安装及使用

Scrapyd的安装及使用

1. 安装scrapyd

         pip install scrapyd 
      

2. 安装setuptools

为什么要安装这个工具?

因为部署的应用需要打包成*.egg才能运行

官网地址:pypi.python.org/pypi/se下载

        pip install setuptools-38.5.2-py2.py3-none-any
      

3. 部署工程

3.1 创建项目

工程下会有一个叫scrapy.cfg的文件,文件的内容如下:
        [settings]
default = my_spider.settings

[deploy:demo]  # demo是指这个deploy的名称,自己命名,可以多个。(后面有用到) 
#url = http://localhost:6800/
project = my_spider  # 工程的名称
      

3.2 启动scrapyd

在本工程下命令行下启动scrapyd

注意: 如果不先启动scrapyd就会无法部署工程

3.3 部署项目

通过scrapyd-deploy部署,要求装一个scrapyd-client
        pip install scrapyd-client
      

3.4 配置scrapyd-deploy

在 %python_home%\Scripts下增加一个scrapyd-deploy.bat文件,内容如下:

        @echo off 
"%python_home%\python.exe" "%python_home%\Scripts\scrapyd-deploy" %1 %2 %3 %4 %5 %6 %7 %8 %9
      

3.5 使用scrapyd-deploy

        scrapy-deploy demo  #demo就是scrapy.cfg中的名字
      

4 运行Spider

        curl http://localhost:6800/schedule.json -d project=项目名 -d spider=爬虫名
      

5 查看效果

在浏览器输入localhost:6800

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值