Py学习  »  Python

左手用R右手Python系列——动态网页抓取与selenium驱动浏览器

金正皓 • 6 年前 • 431 次点击  

关于基础的网络数据抓取相关内容,本公众号已经做过很多次分享,特别是R语言的爬虫框架(RCurl+XML/httr+rvest[xml2+selectr])已经形成了较为丰富的教程系统。

但是所有这些都是基于静态页面的(抓包与API访问的除外),很多动态网页不提供API访问,这样就只能寄希望于selenium这种基于浏览器驱动技术来完成。

好在R语言中已经有了selenium接口包——RSelenium包,这为我们爬取动态网页提供了可能。我在今年年初写过一个实习僧网站的爬虫,那个是使用R语言中另一个基于selenium驱动的接口包——Rwebdriver来完成的。

实习僧招聘网爬虫数据可视化

当时技术不太成熟,思路也比较幼稚,我使用了导航器硬生生的遍历了500页内容,虽然最后也爬完了所有数据,但是耗时较长(将近40分钟),效率比较低。(感兴趣的小伙伴儿可以参考下上面那篇,不过实习僧的官网近期有较大改版,现在爬取难度肯定要比当初难多了!那个代码可能无法使用了)

最近抽时间学习了下RSelenium包的相关内容,这里感谢陈堰平老师在R语言上海大会现场所做《用RSelenium打造灵活强大的网络爬虫》的演讲,虽然未达现场,但是有幸看完视频版,其中的几个细节解决了我近段时间的一些困惑,这里表示感谢。

陈堰平老师主讲:《用RSelenium打造灵活强大的网络爬虫》 www.xueqing.tv/course/88 一个老外关于RSelenium的入门视频(youtobe请自行翻墙): www.youtube.com/watch?v=ic6…

当前R语言中能做到解析动态网页的有以下几个包(欢迎补充):

  • RSelenium(推荐)
  • Rwebdriver(不很成熟)
  • seleniumpipes(结合RSelenium更高效)
  • rdom(高级封装,灵活性不够)
  • Rcrawler(支持多进程)
  • webshot(专门用于动态网页截图)

本节以下内容正式分享今日案例,目标是拉勾网(不要问为什么,因为之前我还没有爬过拉钩)!

在介绍案例之前,请确保系统具备以下条件:

本地有selenium服务器并添加系统路径;
本地有plantomjs浏览器并添加系统路径;
安装了RSelenium包。

因为涉及到自动化点击操作,Chrome浏览器倒腾一下午硬是在点击环节出故障,找到了原因,因为拉勾网页面很长,而下一页按钮不在默认视窗范围内,使用了js脚本控制滑动条失败,原因不明,看到有人用firefox浏览器测试成功,我还没有试过,这里改用plantomjs无头浏览器(无需考虑元素是否被窗口遮挡的问题。)

R语言版:

#!!!这两句是在cmd或者PowerShell中运行的!
#RSelenium服务未关闭之前,请务必保持该窗口状态!
###启动selenium服务:
cd D:\
java -jar selenium-server-standalone-3.3.1.jar
##selenium服务器也可以直接在R语言中启动(无弹出窗口)
system("java -jar \"D:/selenium-server-standalone-2.53.1.jar\"",wait = FALSE,invisible = FALSE)
#加载包
library("RSelenium")
library("magrittr")
library("xml2")

启动服务

#给plantomjs浏览器伪装UserAgent
eCap <- list(phantomjs.page.settings.userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0")
###伪装浏览器UserAgent,为什么即使使用plantomjs这种浏览器也需要伪装UA呢,
###因为plantomjs是专门用于web端页面测试的,通常都是在自己的web项目中测试web端功能,直接拿去抓别人的网站,默认的UA就是plantomjs;
###这是公然的挑衅!

###连接plantomjs服务
remDr <- remoteDriver(browserName = "phantomjs", extraCapabilities = eCap)

构建自动化抓取函数:

#自动化抓取函数:
myresult<-function(remDr,url){
    ###初始化一个数据框,用作后期收据收集之用!
    myresult<-data.frame() 
    ###调用后台浏览器(因为是plantomjs这种无头浏览器(headless),所以你看不到弹出窗口)
    remDr$open()
    ###打开导航页面(也就是直达要抓取的目标网址)
    remDr$navigate(url) 
    ###初始化一个计时器(用于输出并查看任务进度)
    i = 0
    while(TRUE){
        #计时器开始计数:
        i = i+1
        #范回当前页面DOM
        pagecontent<-remDr$getPageSource()[[1]]
        #以下三个字段共用一部分祖先节点,所以临时建立了一个根节点(节省冗余代码)
        con_list_item       <- pagecontent %>% read_html() %>% xml_find_all('//ul[@class="item_con_list"]/li')
        #职位名称
        position.name       <- con_list_item %>% xml_attr("data-positionname") 
        #公司名称
        position.company    <- con_list_item %>% xml_attr("data-company") 
        #职位薪资
        position.salary     <- con_list_item %>% xml_attr("data-salary") 
        #职位详情链接
        position.link       <- pagecontent %>% read_html() %>% xml_find_all('//div[@class="p_top"]/a') %>% xml_attr("href")
        #职位经验要求
        position.exprience  <- pagecontent %>% read_html() %>% xml_find_all('//div[@class="p_bot"]/div[@class="li_b_l"]') %>% xml_text(trim=TRUE) 
        #职位所述行业
        position.industry   <- pagecontent %>% read_html() %>% xml_find_all('//div[@class="industry"]') %>% xml_text(trim=TRUE) %>% gsub("[[:space:]\\u00a0]+|\\n", "",.)
        #职位福利
        position.bonus      <- pagecontent %>% read_html() %>% xml_find_all('//div[@class="list_item_bot"]/div[@class="li_b_l"]') %>% xml_text(trim=TRUE) %>% gsub("[[:space:]\\u00a0]+|\\n", "/",.)
        #职位工作环境
        position.environment<- pagecontent %>% read_html() %>% xml_find_all('//div[@class="li_b_r"]') %>% xml_text(trim=TRUE) 
        #收集数据
        mydata<- data.frame(position.name,position.company,position.salary,position.link,position.exprience,position.industry,position.bonus,position.environment,stringsAsFactors = FALSE)
        #将本次收集的数据写入之前创建的数据框
        myresult<-rbind(myresult,mydata)
        #系统休眠0.5~1.5秒
        Sys.sleep(runif(1,0.5,1.5))
        #判断页面是否到尾部
        if ( pagecontent %>% read_html() %>% xml_find_all('//div[@class="page-number"]/span[1]') %>% xml_text() !="30"){
            #如果页面未到尾部,则点击下一页
            remDr$findElement('xpath','//div[@class="pager_container"]/a[last()]')$clickElement()
            #但因当前任务进度
            cat(sprintf("第【%d】页抓取成功",i),sep = "\n")
        } else {
            #如果页面到尾部则跳出while循环
            break
        }
    }
    #跳出循环后关闭remDr服务窗口
    remDr$close() 
    #但因全局任务状态(也即任务结束)
    cat("all work is done!!!",sep = "\n")
    #返回最终数据
    return(myresult)
}

运行抓取函数

url <- "https://www.lagou.com/zhaopin"
myresult <- myresult(remDr,url)
#预览
DT::datatable(myresult) 

Python:

import os,random,time
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities 
import DesiredCapabilities
from lxml import etree

启动服务

dcap = dict(DesiredCapabilities.PHANTOMJS)
#这里也是伪装一下UA:
dcap["phantomjs.page.settings.userAgent"] = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:25.0) Gecko/20100101 Firefox/25.0")
#启动服务(python里面的selenium内置有selenium服务器,需要本地启动)
driver = webdriver.PhantomJS(desired_capabilities=dcap)

构建抓取函数

def getlaogou(driver,url):
    #初始化一个长度为0的空字典!以备之后收集数据
    myresult = {
              "position_name":[],
              "position_company":[],
              "position_salary":[],
              "position_link":[],
              "position_exprience":[],
              "position_industry":[],
              "position_environment":[]
              };
    #导航到目标网址
    driver.get(url)
    #计时器初始化
    i =0
    while True:
        #计时器累计计时:
        i+=1 
        #获取当前页面DOM
        pagecontent = driver.page_source
        #解析HTML文档
        result = etree.HTML(pagecontent)
        #使用字典内单个list的extend方法累计收集数据
        myresult["position_name"].extend(result.xpath('//ul[@class="item_con_list"]/li/@data-positionname'))
        myresult["position_company"].extend(result.xpath('//ul[@class="item_con_list"]/li/@data-company'))
        myresult["position_salary"].extend(result.xpath('//ul[@class="item_con_list"]/li/@data-salary'))
        myresult["position_link"].extend(result.xpath('//div[@class="p_top"]/a/@href'))
        myresult["position_exprience"].extend([ text.xpath('string(.)').strip() for text in  result.xpath('//div[@class="p_bot"]/div[@class="li_b_l"]')])
        myresult["position_industry"].extend([ text.strip() for text in  result.xpath('//div[@class="industry"]/text()')])
        myresult["position_environment"].extend(result.xpath('//div[@class="li_b_r"]/text()'))
        #单次循环任务休眠
        time.sleep(random.choice(range(3)))
        #判断页面是否到尾部
        if result.xpath('//div[@class="page-number"]/span[1]/text()')[0] != '30':
            #如果未到达页面尾部,则点击下一页:
            driver.find_element_by_xpath('//div[@class="pager_container"]/a[last()]').click()
            #同时打印当前任务 状态!
            print("第【{}】页抓取成功!".format(i))
        else:
            #如果所有页面到达尾部,则跳出循环!
            break
    #打印全局任务状态
    print("everything is OK")
    #退出并关闭selenium服务!
    driver.quit()
    #返回数据
    return pd.DataFrame(myresult)

运行抓取程序

url = "https://www.lagou.com/zhaopin"
mydata = getlaogou(driver,url) 

在线课程请点击文末原文链接:

Hellobi Live | R语言可视化在商务场景中的应用
往期案例数据请移步本人GitHub:
github.com/ljtyduyu/Da…


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/Rex3WAlzrB
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/4537
 
431 次点击