python爬虫动态加载页面_数据之路 - Python爬虫 - 动态页面

一、Ajax数据爬取

1.Ajax介绍

Ajax,全称为Asynchronous JavaScript and XML,即异步的JavaScript和XML。 它不是一门编程语言,而是利用JavaScript在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。发送Ajax请求到网页更新过程,简单分为以下3步:发送请求;解析内容;渲染网页。

Ajax具有特殊的请求类型,它叫作xhr。

2.Ajax数据爬取

#首先,定义一个方法来获取每次请求的结果。 在请求时,page是一个可变参数,所以我们将它作为方法的参数传递进来,相关代码如下:

from urllib.parse importurlencodeimportrequests

base url= 'https://m.weibo.cn/api/container/getlndex?'headers={'Host': 'm.weibo.cn','Referer':'https://m.weibo.cn/u/2830678474','User-Agent':'Mozilla/s.o (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko)

Chrome/58.0.3029.110 Safari/537.36','X-Requested-With ':'XMLHttpRequest',

}def get_page(page)':

params ={'type':'uid','value':'2830678474','containerid':'1076032830678474','page': page

}

url= base_url +urlencode(params)try:

response= requests.get(url, headers=headers)if response.status_code == 200:returnresponse.json()exceptrequests.ConnectionError as e:print('Error', e.args)#随后,我们需要定义一个解析方法,用来从结果中提取想要的信息,比如这次想保存微博的id、正文、赞数、 评论数和转发数这几个内容,那么可以先遍历cards,然后获取mblog 中的各个信息,赋值为一个新的字典返回即可:

from pyquery importPyQuery as pqdefparse _page (j son):ifjson:

items= j son. get('data') .get('cards')for item initems:

item= item.get('mblog')

weibo={}

weibo['id'] = item.get('id')

weibo['text'] = pq(item.get('text')). text()

weibo['attitudes'] = item.get('attitudes_count')

weibo ['comments'] = item.get('comments_count')

weibo['reposts'] = item.get('reposts_count')yieldweibo#最后,遍历一下page,一共10页,将提取到的结果打印输出即可:

if name == 'main':for page in range(l, 11):

json=get_page(page)

results=parse_page(json)for result inresults:print(result)#加一个方法将结果保存到MongoDB数据库:

from pymongo importMongoClient

client=MongoClient ()

db= client [ 'weibo']

collection= db['weibo']defsave_to_mongo(result):ifcollection.insert(result):print('save to Mongo')

二、Selenium库

Selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。

1.声明浏览器对象

from selenium importwebdriver

browser=webdri ver. Chrome()

browser=webdriver. Firefox()

browser=webdri ver. Edge()

browser=webdriver. PhantomJS()

browser=webdriver.Safari()#完成浏览器对象初始化并将其赋值为browser 对象。调用 browser对象,让其执行各个动作以模拟浏览器操作。

2.访问页面

from selenium importwebdnver

browser=webdriver.Chrome()

browser. get (’https://www.taobao.com’ )print(browser.page_source)

browser. close()#用get()方法来请求网页,参数传入链接URL即可。

3.查找节点

#单个节点

find_element_by_id

find_element_by_name

find_element_by_xpath

find_element_by_link_text

find_element_by_partial_link_text

find_element_by_tag_name

find_element_by_class_name

find_element_by_css_selector

Selenium提供通用方法find_element(),它需要传入两个参数: 查找方式By和值。find_element(By.ID,id)from selenium importwebdriverfrom selenium.webdriver.common.by importBy

browser=webdriver.Chrome()

browser.get('https://www.taobao.com')

input_first= browser.find_element(By.ID,'q')print(input_first)

browser.close()#多个节点

要查找所有满足条件的节点,需要用find_elements()这样的方法

4.节点交互

Selenium可以驱动浏览器来执行一些操作,也就是说可以让浏览器模拟执行一些动作。

from selenium importwebdriverimporttime

browser=webdriver.Chrome()

browser.get('https://www.taobao.com')

input= browser.find_element_by_id('q')#send_keys()方法用于输入文字

input.send_keys('iPhone')

time.sleep(1)#clear()方法用于清空文字

input.clear()

input.send_keys('iPad')

button= browser.find_element_by_class_name('btn-search')#click()方法用于点击按钮

button.click()

5.动作链

如鼠标拖曳、 键盘按键等,它们没有特定的执行对象,这些动作用另一种方式来执行,那就是动作链。-实现一个节点的拖曳操作,将某个节点从一处拖曳到另外一处#首先,打开网页中的一个拖曳实例,然后依次选中要拖曳的节点和拖曳到的目标节点,接着声明ActionChains对象并将其赋值为actions变量,然后通过调用actions变量的drag_and_drop()方法,再调用perform()方法执行动作,此时就完成了拖曳操作

from selenium importwebdnverfrom selenium.webdriver importActionChains

browser=webdriver.Chrome()

url =’http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'browser.get(url)

browser.switch_to.frame('iframeResult')

source= browser.find_element_by_css selector('#draggable')

target= browser.find_element_by_css_selector('#droppable')

actions=ActionChains(browser)

actions.drag_and_drop(source, target)

actions.perform()

6.JavaScript执行

对于某些操作,Selenium API并没有提供。 比如,下拉进度条,它可以直接模拟运行JavaScript,

此时使用execute script()方法即可实现,代码如下:

from selenium importwebdriver

browser=webdriver.Chrome()

browser. get (’https://www.zhihu.com/explore')

browser.execute_script('window.scrollTo(o, document.body.scrollHeight)’)

browser.execute_script('alert(”To Bottom”)')#这里就利用execute script()方法将进度条下拉到最底部,然后弹出alert提示框。

7.获取节点信息

- 获取属性

#使用get_attribute()方法来获取节点的属性

from selenium importwebdriverfrom selenium.webdriver importActionChains

browser=webdri ver. Chrome()

url= 'https://www.zhihu.com/explore'browser. get ( url)

logo= browser.find_element_by_id(’zh-top-link-logo’)print(logo)print(logo.get_attribute(’class'))

- 获取文本值

from selenium importwebdriver

browser=webdriver.Chrome()

url=’https://www.zhihu.com/explore’

browser. get(url)

input= browser.find_element by class name('zu-top-add-question’)

print(input.text)

- 获取id、位置、标签名和大小

from. selenium importwebdnver

browser=webdriver. Chrome()

url =’https://www.zhihu.com/explore'browser.get (url)

input= browser.find_element_by_class_name(’zu-top-add-question')

print(input.id)    #节点id

print(input.location)  #节点页面相对位置

print(input.tag_name)  #标签名称

print(input.size)   #节点大小

8.Frame切换

网页中有一种节点叫作iframe,也就是子Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。 Selenium打开页面后,它默认是在父级Frame里面操作,而此时如果页面中还有子Frame,它是不能获取到子Frame里面的节点的。 这时就需要使用switch_to.frame()方法来切换Frame。

importtimefrom selenium importwebdriverfrom selenium.common.exceptions importNoSuchElementException

browser=webdriver.Chrome()

url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'

browser.get(url)

browser.switch_to.frame('iframeresult’)

try:

logo= browser.find_element_by_class_name('logo')exceptNoSuchElementException:print(’NO LOGO')

browser.switch_to.parent_frame)

logo=browser. find_element_by_class_name('logo')print(logo)print(logo.text)

首先通过switch_to. frame()方法切换到子Frame里面,然后尝试获取父级Frame 里的logo 节点(这是不能找到的),如果找不到的话,就会抛出NoSuchElementException异常,异常被捕捉之后,就会输出NO LOGO。 接下来,重新切换回父级Frame,然后再次重新获取节点,发现此时可以成功获取了。

- 隐式等待

使用隐式等待执行测试的时候,如果Selenium没有在DOM中找到节点,将继续等待,超出设定时间后,则抛什1找不到节点的异常。 换句话说,当查找节点而节点并没有立即出现的时候,隐式等待将等待一段时间再查找DOM,默认的时间是0。

from selenium importwebdriver

browser=webdriver.Chrome()

browser.implicitly_wait (10)

browser.get(’https://www.zhihu.com/explore')

input = browser. find_element_by_class_name(’zu-top-add-question’)print(input)

- 显式等待

隐式等中认为固定了等待时间,然而页面的加载时间会受到网络条件的影响。

显式等待方法,它指定要查找的节点,然后指定一个最长等待时间。 如果在规定时间内加载出这个节点,就返回查找的节点;如果到了规定时间依然没有加载出来,则抛出超时异常。

#首先引入WebDriverWait这个对象,指定最长等待时间,然后调用它的until()方法,传入要等待条件expected_conditions。 比如,这里传入了presence_of_element_located这个条件,代表节点出现的意思,其参数是节点的定位元组,也就是ID为q的节点搜索框。

-效果:在10秒内如果ID为q的节点(即搜索框)成功加载出来,就返回该节点;如果超过10秒还没有加载出来,就抛出异常。from selenium importwebdriverfrom selenium.webdriver.common.by importByfrom selenium.webdriver.support.ui importWebDriverWaitfrom selenium.webdriver.support importexpected_conditions as EC

browser=webdriver.Chrome()

browser.get(’https://www.taobao.com/’)

wait= WebDriverWait(browser, 10)

input=wait. until(EC. presence_of _element_located( (By. ID,’q’)))

button= wait.until(EC.element to be clickable((By.CSS_SELECTOR,’.btn search')))

print(input, button)

9.前进、后退

浏览器时都有前进和后退功能,Selenium中分别通过forward()、back()方法实现。

importtimefrom selenium importwebdnver

browser=webdriver.Chrome()

browser. get (’https://www.baidu.com/’)

browser.get('https://www.taobao.com/’)

browser.get(’https://www.python.org/’)

browser.back()

time.sleep(l)

browser. forward()

browser. close()

10.Cookies

from selenium importwebdriver

browser=webdriver.Chrome()

browser.get('https://www.zhihu.com/explore')print(browser.get_cookies())

browser.add_cookie({'name':'name','domain':'www.zhihu.com','value':'germey'})

browser.delete_all_cookies()

11.选项卡管理

importtimefrom selenium importwebdnver

browser=webdriver. Chrome()

browser.get(’https://www.baidu.com')

browser.execute_script(’window. open()’)print(browser. window _handles)

browser.switch_to_window(browser.window_handles[l])

browser.get(’https://www.taobao.com')

time.sleep(l)

browser.switch_to_window(browser.window_handles(0))

browser.get(’https://python.org')

12.异常处理

from selenium importwebdriverfrom selenium.common.exceptions importTimeoutException, NoSuchElementExcephon

browser=webdriver.Chrome()try:

browser.get('https://www.baidu.com’)

exceptTimeoutException:print('Time Out')try:

browser. find_element_by_id('hello')exceptNoSuchElementException:print(’No Element’)finally:

browser.close()

三、Splash

Splash是一个JavaScript渲染服务,是一个带有HTTPAPI的轻量级浏览器,它对接了Python中的Twisted和QT库。 利用它,我们同样可以实现动态谊染页面的抓取。

异步方式处理多个网页渲染过程;

获取渲染后的页面的源代码或截图;

通过关闭图片渲染或者使用Adblock规则来加快页面渲染速度;

可执行特定的JavaScript脚本;

可通过Lua脚本来控制页面渲染过程;

获取渲染的详细过程并通过HAR( HTTP Archive )格式呈现

1.Splash Lua脚本

Splash可以通过Lua脚本执行一系列渲染操作,这样我们就可以用Splash来模拟类似Chrome、PhantomJS的操作。

- 入口及返回值

function main(splash, args)

splash:go("http://www.baidu.com")

splash:wait(o.s)

local title=splash:evaljs(”document.title”)return {title=title}

end

- 异步处理

function main(splash, args)

local example_urls= {"www.baidu.com”,”www.taobao.com”,”www.zhihu.com'’}

local urls = args.urls orexample_urls

local results={}for index,url inipairs(urls) do

local ok, reason= splash:go(”http://”..url)ifok then

splash:wait(2)

results[url]=splash:png()

end

endreturnresults

end

2.Splash对象属性

- args

#此属性可获取加载时配置的参数,如URL

function main(splash, args)

local url=args.url

end

- js_enabled

#此属性是Splash的JavaScript执行开关,可以将其配置为true或false来控制是否执行JavaScript代码,默认为true。

function main(splash, args)

splash:go(”https://www.baidu.com”)

splash.js_enabled=false

local title= splash:evaljs(”document.title")

return {title=title}

end

- resource_timeout

#此属性可以设置加载的超时时间,单位是秒。 如果设置为0或nil,代表不检测超时。

function main(splash)

splash.resource_timeout= 0.1

assert(splash:go('https://www.taobao.com'))returnsplash:png()

end

- images_enabled

#此属性可以设置图片是否加载,默认情况下是加载的。

function main(splash, args)

splash.images_enabled=falseassert(splash:go(’https://www.jd.com'))

return {png=splash:png()}

end

- plugins_enabled

#此属性可以控制浏览器插件是否开启。 默认情况下,此属性是false,表示不开启。

splash.plugins_enabled = true/false

- scroll_position

#通过设置此属性,可以控制页面上下或左右滚动。

function main(splash, args)assert(splash:go('https://www.taobao.com’))

splash.scroll_position = {y=400}return {png=splash: png()}

end

3.Splash对象方法

go() #用于请求某个链接,模拟GET和POST请求。

ok, reason = splash#go{url, baseurl=nil, headers=nil, http_method="GET", body=nil, formdata=nil}

baseurl是资源加载相对路径,headers是请求头,http_method默认GET,body发POST请求时的表单数据,formdata是POST时的表单数据

wait()#控制页面等待时间

jsfunc() #可以直接调用JavaScript定义的方法,但是所调用的方法需要用双中括号包围,这相当于实现了JavaScript方法到Lua脚本的转换。

evaljs() #执行JavaScript代码并返回最后一条JavaScript语句的返回结果

runjs() #执行JavaScript代码,它与evaljs()的功能类似,但是更偏向于执行某些动作或声明某些方法。

autoload() #设置每个页面访问时自动加载的对象

call_later() #通过设置定时任务和延迟时间来实现任务延时执行,井且可以在执行前通过cancel()重新执行定时任务。

http_get() #模拟发送HTTP的GET请求

http_post() #用来模拟发送POST请求

set_content() #用来设置页面的内容

html() #用来获取网页的源代码

png() #用来获取PNG格式的网页截图

jpeg() #用来获取JPEG格式的网页截图

har() #用来获取页面加载过程

url() #获取当前正在访问的URL

get_cookies() #获取当前页面的Cookies

add_cookies() #为当前页面添加Cookie

clear_cookies() #清除所有的Cookies

get_viewport_size() #获取当前浏览器页面的大小,即宽高

set_viewport_size() #设置当前浏览器页面的大小,即宽高

set_viewport_full() #设置浏览器全屏显示

set_user_agent() #设置浏览器的User-Agent

set_custom_headers() #设置请求头

select() #选中符合条件的第一个节点,如果有多个节点符合条件,则只会返回一个,其参数是css选择器

select_all() #选中所有符合条件的节点,其参数是css选择器

mouse_click() #模拟鼠标点击操作,传入的参数为坐标值x和y。此外,也可以直接选中某个节点,然后调用此方法

4.Splash API调用

render.html #此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称

importrequests

url= 'http://localhost:8050/render.html?url=https://www.baidu.com'response=requests.get(url)print(response.text)

render.pug#此接口可以获取网页截图,它返回的是PNG格式的图片二进制数据

importrequests

url ='http://localhost:8050/render.png?url=https://www.jd.com&wait=S&width=lOOO&height=700'response=requests.get(url)

with open(’taobao.png’,'wb ’) as f:

f.write(response.content)

render.jpeg#此接口可以获取网页截图,它返回的是JPEG格式的图片二进制数据

render.bar #此接口用于获取页面加载的HAR数据

render.json #此接口包含了前面接口的所有功能,返回结果是JSON格式

execute #此接口可用于实现与Lua脚本的对接。

5.Splash负载均衡配置

用Splash做页面抓取时,如果爬取的量非常大,任务非常多,用一个Splash服务来处理的话,未免压力太大了,此时可以考虑搭建一个负载均衡器来把压力分散到各个服务器上。 这相当于多台机器多个服务共同参与任务的处理,可以减小单个Splash服务的压力。

第一步:配置Splash服务;第二步:配置负载均衡,选用任意一台带有公网IP的主机来配置负载均衡。首先,在这台主机上装好Nginx,然后修改Nginx的配置文件nginx.conf;第三步:配置认证,Splash是可以公开访问的,如果不想让其公开访问,还可以配置认证,这仍然借助于Nginx。可以在server的location字段中添加auth_basic和auth basic user_file字段;第四步:测试。

http {

upstream splash {

least_conn;

server41.159.27.223:8050;

server41.159. 27. 221: 8050;

server41.159. 27. 9: 8050:

server41.159 .117 .119: 8050;

}

server {

listen8050;

location/{

proxy_pass http://splash;

auth basic ”Restricted";

auth basic user_file /etc/nginx/conf.d/.htpasswd;

}

}

}importrequestsfrom urllib.parse importquoteimportre

lua= '''function main(splash, args)

local treat = require("treat'’)

local response = splash:http_get(’'http://httpbin.org/get")

return treat.as_string(response.body

end'''url= 'http://splash:8050/execute?lua_source=’+ quote(lua)

response = requests.get(url, auth=('admin','admin’))

ip = re.search('(\d+\.\d+\.\d+\.\d+)', response.text).group(l)print(ip)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值