作为一个爬虫小白,学完了爬取静态网页的基本思路和相关库,于是便开始着手学习如何爬取动态页面。
动态网页的定义,在这里就不详细解释了,有疑问的可以自行百度。
首先,爬取动态页面,一般有两种方法:
1. 有的网页向服务器发出请求,会返回json格式的数据,这个数据里就包含着你要爬取的内容,你只要拿到这个json数据,进行相关处理就行。
2. 有的网页想直接拿到这个json数据,却不是那么容易,于是只好采取selenium+PhantomJS的思路。
准备工作
首先,我们来看一下,王者荣耀官网的源码,官网(pvp.qq.com/web201605/wallpaper.shtml ),我用的是火狐浏览器,点击右键查看网页源代码,按Ctrl+F搜索“仲夏夜之梦”,发现没有结果,明明在网页上有的内容,却在源码里找不到,这说明这个网页就是一个动态的页面,有些人也许会疑惑,为什么按F12,却可以找到呢?这是因为,F12所呈现出来的是已经将数据渲染好的页面。
使用selenium+PhantomJS的基本思路就是既然requests库拿到的只是没有内容的框架,那我就模拟浏览器的行为,这样所拿到的页面就是已经将数据渲染完成的页面了。然后用BeautifulSoup库对拿到的页面代码进行处理就行。
大概原理懂了之后,就可以开始爬取了,首先你要安装selenium和PhantomJS,我的python版本是3.x(具体多少忘了),直接打开命令行,pip install selenium就行,至于PhantomJS,可能要花点功夫,网上都有教程,直接百度就好,PhantomJS是一个无头浏览器,没有UI界面,所以我推荐可以再下一个火狐驱动,这样你可以看到页面的跳转等具体是一个什么样子,Firefox的驱动geckodriver 下载地址:github.com/mozilla/geckodriver/releases/ ,详细步骤网上可以搜索教程。
具体操作
现在已经默认大家安装好所需的工具,接下来就可以开始爬取了。为了更好地感受什么叫模拟浏览器的行为(其实就是模拟人的点击,下拉等行为),我们从pvp.qq.com/ 这个页面开始。
from selenium import webdriver
driver = webdriver.PhantomJS()
url = 'http://pvp.qq.com/'
driver.get(url)
print(driver.page_source)
driver.close()
这就是最基本的selenium+PhantomJS语法了,我用的直接是IDLE,注意按F5运行时,弹出的小黑框,千万不要关闭,不想看它的话直接最小化,否则就会报错(不要问我是怎么知道的,我猜是不是相当于直接关闭了浏览器。上述代码实现了获取对应url的内容,并将页面代码输出,很直观易懂,如果不是动态页面,直接用requests库也可以了,但是它的强大就在于模拟人的行为。
还是这个网页,接下来我们要跳转到游戏壁纸的界面。
将鼠标移到游戏资料tab,然后点击游戏壁纸,就会跳到壁纸页面,接下来就要用selenium模拟行为了。代码如下
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.PhantomJS()
url = 'http://pvp.qq.com/'
driver.get(url)
#1,找到游戏资料这个元素,xpath语法进行元素定位
moveElement = driver.find_element_by_xpath('//a[@title="游戏资料"]')
#2.模拟鼠标悬浮的行为,鼠标事件
ActionChains(driver).move_to_element(moveElement).perform()
#3.点击游戏壁纸这个链接
driver.find_element_by_xpath('//a[@title="游戏壁纸"]').click()
print(driver.page_source)
代码中给出了每一步的解释,注意1,2步是不能省略的,否则会报“定位不到游戏壁纸这个元素”(可以试一下),很好理解,模拟人的行为嘛,如果你的鼠标不移上去,肉眼也看不到“游戏壁纸”这个元素啊。
如果细说selenium的鼠标事件和元素定位(很重要),又要占很大的篇幅,这篇文章旨在给读者一个大的轮廓和指引,个中细节还需要自己去查阅资料。
如果电脑有火狐浏览器以及安装好火狐驱动的读者,强烈建议将
driver = webdriver.PhantomJS()
改成
driver = webdriver.Firefox()
运行一下,相信你会更加理解什么叫模拟人的行为,但细心的读者会发现,此时输出的网页源码仍然是前一个网页的代码而不是当前壁纸页面的,明明浏览器已经跳转到壁纸页面,为什么却不是当前源码,老实说,我也不清楚,但我知道解决方法(手动斜眼笑
这里又要说一个概念了,那就是句柄(请自行百度),这里放上代码试验一下
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.PhantomJS()
url = 'http://pvp.qq.com/'
driver.get(url)
#输出当前句柄
print(driver.current_window_handle)
moveElement = driver.find_element_by_xpath('//a[@title="游戏资料"]')
ActionChains(driver).move_to_element(moveElement).perform()
driver.find_element_by_xpath('//a[@title="游戏壁纸"]').click()
#已经跳转页面了,输出当前句柄
print(driver.current_window_handle)
#输出所有句柄
print(driver.window_handles)
运行结果如下
可以发现,虽然跳转页面了,但是当前句柄却没有发生变化,什么原因我也不知道,如果有大神知道请告诉我(PS.这里我又是使用PhantomJS(),如果使用Firefox()可能不会出现上述结果)
接下来只要
all_h = driver.window_handles
driver.switch_to.window(all_h[1])
print(driver.page_source)
手动转到第二个句柄,输出源码
已经拿到壁纸页面的源码了(有可能只运行一次拿不到,我也不知道为什么,有可能是服务器的问题?)
接下来只要用BeautifulSoup库对爬到的页面进行解析处理就行了,这里也就不详细展开了。
直接将代码粘贴如下:
import os
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from bs4 import BeautifulSoup
import requests
import time
driver = webdriver.PhantomJS()
driver.maximize_window()
url = 'http://pvp.qq.com/'
driver.get(url)
moveElement = driver.find_element_by_xpath('//a[@title="游戏资料"]')
ActionChains(driver).move_to_element(moveElement).perform()
driver.find_element_by_xpath('//a[@title="游戏壁纸"]').click()
all_h = driver.window_handles
driver.switch_to.window(all_h[1])
time.sleep(3)
os.chdir("D://王者荣耀")
page_num = 0
while page_num < 12:
soup = BeautifulSoup(driver.page_source, 'html.parser')
Lists = soup.find_all('div', {'class': 'p_newhero_item'})
titleLists = []
hrefLists = []
for item in Lists:
subSoup = BeautifulSoup(str(item), 'html.parser')
titleList = subSoup.find('h4')
titleLists.append(titleList.text)
linkList = subSoup.find('li', {'class': 'sProdImgL6'})
soup = BeautifulSoup(str(linkList), 'html.parser')
a = soup.find('a')
hrefLists.append(a['href'])
for i in range(len(titleLists)):
url = hrefLists[i]
r = requests.get(url).content
path = titleLists[i] + '.jpg'
try:
with open(path, 'wb') as f:
f.write(r)
print('保存成功 %s.jpg' % titleLists[i])
except:
print('保存失败')
driver.find_element_by_xpath('//a[@class="downpage"]').click()
time.sleep(3)
page_num = page_num + 1
driver.close()
需要注意的是,在爬取下一页壁纸的时候,在点击下一页这个元素后,需要time.sleep(),等加载完全,才能接着爬取,会发现有四张照片会被爬取很多遍,这是网页决定的,可以选择链接去重,因为相同名字的图片会被覆盖,所以这里就没有去管它,但有时链接去重是很重要的,电脑桌面是1920*1080的,所以我爬取了对应的图片链接
放上运行结果
需要事先在D盘建一个“王者荣耀”的文件夹
综上,便是爬取王者荣耀游戏壁纸的步骤了,没有对代码进行整合重构,第一次记录,可能该详细的地方没有详细,该简略的地方却很啰嗦,请包涵,感谢看到最后。