文章目录
爬虫基础部分Ⅲ
上一部分了解了Ajax的分析和抓取方式,但有些网页比如淘宝,其Ajax接口含有很多加密参数,我们难以直接找出其规律,也很难直接分析Ajax来抓取。
为了解决这些问题,我们可以直接使用 模拟浏览器运行 的方式来实现,这样就可以做到在浏览器中看到的是什么样,抓取的源码就是什么样,也就是可见即可爬。这样就不用管网页内部的JS用了什么算法去渲染、加载页面,也不用管网页后台的Ajax接口到底有哪些参数。
selenium
懒得截图了(每次截图都弄不好TAT)
(1) selenium是什么
它是一个强大的库,首先你需要去安装它pip install selenium
。它可以用几行代码,控制浏览器,做出自动打开、输入、点击等操作,就像是有一个真正的用户在操作一样。
而对于那些交互复杂、加密复杂的网站,selenium
问题简化,爬动态网页如爬静态网页一样简单。
而关于动态网页和静态网页,其实你都已经接触过了。
前边第Ⅰ部分用html
写出的网页,就是静态网页。我们使用BeautifulSoup
爬取这类型网页,因为网页源代码中就包含着网页的所有信息(第0个请求里,在preview
里就能看到所有信息),因此,网页地址栏的URL
就是网页源代码的URL
。
后来在第Ⅱ部分,开始接触更复杂的网页,比如QQ音乐,要爬取的数据不在HTML源代码中,而是在json
中,你就不能直接使用网址栏的URL
了,而需要找到json
数据的真实URL
。这就是一种动态网页。
不论数据存在哪里,浏览器总是在向服务器发起各式各样的请求,当这些请求完成后,它们会一起组成开发者工具的Elements
中所展示的,渲染完成的网页源代码。
(2) 好处与不足
在遇到页面交互复杂或是URL
加密逻辑复杂的情况时,selenium
就派上了用场,它可以真实地打开一个浏览器,等待所有数据都加载到Elements
中之后,再把这个网页当做静态网页爬取就好了。
说了这么多优点,使用selenium
时,当然也有美中不足之处。
由于要真实地运行本地浏览器,打开浏览器以及等待网渲染完成需要一些时间,selenium
的工作不可避免地牺牲了速度和更多资源,不过,至少不会比人慢。
(3) 如何使用它
1. 下载浏览器驱动
首先呢你需要下载好Chrome浏览器
,然后去http://npm.taobao.org/mirrors/chromedriver/2.44/这里,下载好浏览器驱动,再放到 python的安装目录下。然后本地运行一下下边的代码,有浏览器弹出来打开的是百度首页并搜索了“Python”,下拉到最底部,最后弹出了一个提示,那就安装成功了(/≧▽≦)/
# -*- coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver = webdriver.Chrome()
driver.get('http://www.baidu.com')
Search = driver.find_element_by_id('kw')
Search.send_keys('Python')
time.sleep(1)
Search.send_keys(Keys.ENTER)
time.sleep(2)
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(2)
driver.execute_script('alert("To Bottom")')
time.sleep(2)
driver.close()
2. 设置浏览器引擎
# 本地Chrome浏览器设置方法
from selenium import webdriver #从selenium库中调用webdriver模块
driver = webdriver.Chrome()
# 设置引擎为Chrome,真实地打开一个Chrome浏览器(然后赋值给变量driver,它就是一个实例化的浏览器)
3. 获取数据
我们前面使用BeautifulSoup
解析网页源代码,然后提取其中的数据。selenium
库同样也具备解析数据、提取数据的能力。它和BeautifulSoup
的底层原理一致,但在一些细节和语法上有所出入。selenium
所解析提取的,是Elements
中的所有数据,而BeautifulSoup
所解析的则只是Network
中第0个请求的响应。而用selenium
把网页打开,所有信息就都加载到了Elements
那里,之后,就可以把动态网页用静态网页的方法爬取了(我不是复读机)。
再拿QQ音乐https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E5%91%A8%E6%9D%B0%E4%BC%A6来做例子,看注释:
from selenium import webdriver
import time
driver = webdriver.Chrome() #设置浏览器
url = 'https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E5%91%A8%E6%9D%B0%E4%BC%A6'
driver.get(url)#get(URL)是webdriver的一个方法,它的使命是为你打开指定URL的网页。
time.sleep(2)#由于浏览器缓冲加载网页需要耗费一些时间,等待1-2秒再去解析和提取比较稳妥。
driver.close()
4.解析、提取数据
先回想下,使用BeautifulSoup
从获取的数据中提取数据时,首先要把Response
对象解析为BeautifulSoup
对象,然后再从中提取数据。
而在selenium
中,获取到的网页存在了driver
中,而后,获取和解析是同时做的,都是由driver
这个实例化的浏览器完成。也就是说解析数据是由driver
自动完成的(8用我们管),提取数据是driver
的一个方法:
方法 | 作用 |
---|---|
find_element_by_tag_name | 以上就是提取单个元素的方法了 |
find_element_by_class_name | 见名知意,无需多言! |
find_element_by_id | 而要提取多个元素,只需要把 element 换成 elements 就好啦 |
find_element_by_name | 还要注意不能定位复合类名,如不能find_element_by_class_name(‘xxx xxx’) |
find_element_by_link_text | |
find_element_by_partial_link_text |
提取出的元素是<class 'selenium.webdriver.remote.webelement.WebElement'>
这个类的,它与BeautifulSoup
中的Tag
对象类似,也有提取文本和属性值的属性,下面放WebElement对象与Tag对象的用法对比:
WebElement | Tag | 作用 |
---|---|---|
WebElement.text | Tag.text | 提取文字 |
WebElement.get_attribute() | Tag[ ] | 填入属性名,返回属性值 |
示例一下:
# 以下方法都可以从网页中提取出'你好啊!'这段文字-------------------
find_element_by_tag_name:通过元素的名称选择
# 如<h1>你好啊!</h1>
# 可以使用find_element_by_tag_name('h1')
find_element_by_class_name:通过元素的class属性选择
# 如<h1 class="title">你好啊!</h1>
# 可以使用find_element_by_class_name('title')
find_element_by_id:通过元素的id选择
# 如<h1 id="title">你好啊!</h1>
# 可以使用find_element_by_id('title')
find_element_by_name:通过元素的name属性选择
# 如<h1 name="hello">你好啊!</h1>
# 可以使用find_element_by_name('hello')
#以下两个方法可以提取出超链接------------------------------------
find_element_by_link_text:通过链接文本获取超链接
# 如<a href="spidermen.html">你好啊!</a>
# 可以使用find_element_by_link_text('你好啊!')
find_element_by_partial_link_text:通过链接的部分文本获取超链接
# 如<a href="https://localprod.pandateacher.com/python-manuscript/hello-spiderman/">你好啊!</a>
# 可以使用find_element_by_partial_link_text('你好')
5. 示例
还是QQ音乐周杰伦的歌曲名https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E5%91%A8%E6%9D%B0%E4%BC%A6,这里只提取第一页的,用selenium就不需要去json中找啦~!
# -*- coding: utf-8 -*-
from selenium import webdriver
import time
driver = webdriver.Chrome() #设置浏览器
url = 'https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E5%91%A8%E6%9D%B0%E4%BC%A6'
driver.get(url)#get(URL)是webdriver的一个方法,它的使命是为你打开指定URL的网页。
time.sleep(2)#由于浏览器缓冲加载网页需要耗费一些时间,等待1-2秒再去解析和提取比较稳妥。
# 直接定位提取数据
song_ul = driver.find_element_by_class_name('songlist__list')
song_li = song_ul.find_elements_by_class_name('js_songlist__child')
for song in song_li:
name = song.find_element_by_class_name('songlist__songname_txt')
print(name.text.strip())
driver.close()
6. selenium 与 BS 的配合使用
先用selenium获取到渲染完整的网页源代码,再以字符串形式返回给BS拿去解析和提取。(自己想为啥)
如何获取呢?也是使用driver
的一个方法:page_source
HTML源代码字符串 = driver.page_source
示例:
# -*- coding: utf-8 -*-
from selenium import webdriver
import time,bs4
driver = webdriver.Chrome() #设置浏览器
url = 'https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E5%91%A8%E6%9D%B0%E4%BC%A6'
driver.get(url)#get(URL)是webdriver的一个方法,它的使命是为你打开指定URL的网页。
time.sleep(2)#由于浏览器缓冲加载网页需要耗费一些时间,等待1-2秒再去解析和提取比较稳妥。
# 用BS解析网页
song = bs4.BeautifulSoup(driver.page_source,'html.parser')
# 直接定位提取数据
song_ul = song.find(class_='songlist__list')
song_li = song_ul.find_all(class_='js_songlist__child')
for song in song_li:
name = song.find(class_='songlist__songname_txt')
print(name.text.strip())
driver.close()
没用selenium之前,我们只用BS解析是爬不到歌曲信息的,而现在就可以了,想想为什么
7. selenium节点交互方法
.send_keys('你想输入的内容') # 模拟按键输入,自动填写表单
#你只需要定位到那个输入的框框那里,再用这个方法就可以往输入框里输入
.click() # 点击元素
#定位到能点的地方,基本上都能点
.clear() # 清空元素的内容
#清空你send_keys()里边输入的
更多的操作可以参见官方文档的交互动作介绍:这里
示例,打开百度搜索"Python":
# -*- coding: utf-8 -*-
from selenium import webdriver
import time
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome() #设置浏览器
url = 'http://www.baidu.com'
driver.get(url)#get(URL)是webdriver的一个方法,它的使命是为你打开指定URL的网页。
time.sleep(2)#由于浏览器缓冲加载网页需要耗费一些时间,等待1-2秒再去解析和提取比较稳妥。
#定位<input>标签
baidu = driver.find_elements_by_tag_name('input')
#输入框是第7个
baidu[7].send_keys('Python')
time.sleep(1)
#搜索是第8个,并点击
baidu[8].click()
# 也可以用下面的按下回车事件来代替点击搜索按钮
# baidu[7].send_keys(Keys.ENTER)
time.sleep(3)
driver.close()
8. 设置为无界面模式
需要Chrome升级到59版本及以上。
# 本地Chrome浏览器的静默默模式设置:
from selenium import webdriver #从selenium库中调用webdriver模块
from selenium.webdriver.chrome.options import Options # 从options模块中调用Options类
chrome_options = Options() # 实例化Option对象
chrome_options.add_argument('--headless') # 把Chrome浏览器设置为静默模式
driver = webdriver.Chrome(options = chrome_options) # 设置引擎为Chrome,在后台默默运行
这样浏览器就在后台默默运行,也不嫌我们挡着它。
存储数据
例子:知乎大v张佳玮的文章“标题”、“摘要”、“链接”,并存储到本地文件。需要安装相关模块。上码看注释:
(1) 写入xlsx文件
import requests
import openpyxl
from bs4 import BeautifulSoup
import csv
headers = {
'referer': 'https://www.zhihu.com/people/zhang-jia-wei/posts/posts_by_votes',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36'
}
#HTML里只放了两篇文章,然后去XHR里面找找看,有两个article的请求,在第二个找到了第一页的detail,可用json提取
#再比较各页参数,用循环写就可以获取多页的detail了
list1 = [] #用于储存detail
n = 1
for i in range(3): #这里找两页,否则会给对方服务器造成很大压力
params = {
'include': 'data[*].comment_count,suggest_edit,is_normal,thumbnail_extra_info,thumbnail,can_comment,comment_permission,admin_closed_comment,content,voteup_count,created,updated,upvoted_followees,voting,review_info,is_labeled,label_info;data[*].author.badge[?(type=best_answerer)].topics',
'offset': str(i*20),
'limit': str(20),
'sort_by': 'voteups'
}
res = requests.get('https://www.zhihu.com/api/v4/members/zhang-jia-wei/articles',headers=headers,params=params)
#print(res.status_code)
detaillist = res.json()
articlelist = detaillist['data']
for article in articlelist:
# 把title、url、excerpt写成列表,后边用append函数一行行写入Excel
list1.append([article['title'],article['url'],article['excerpt']])
#开始储存
#创建工作薄
wb = openpyxl.Workbook()
#获取工作薄的活动表
sheet = wb.active
#工作表重命名
sheet.title = 'zjw'
#加表头,给A1单元格B1...赋值
sheet['A1'] = '文章标题'
sheet['B1'] = '文章链接'
sheet['C1'] = '摘要'
for i in list1:
sheet.append(i) #每次写入一行
wb.save('zhihuspider.xlsx') #记得保存格式为xlsx
然后在该py文件目录下,就会有一个zhihuspider.xlsx文件啦!
(2) 写入csv文件
#或者用csv储存
csv_file = open('zhihuspider.csv','w',newline='',encoding='utf-8')
writer = csv.writer(csv_file) #用csv.writer()函数创建一个writer对象。
list2 = ['标题','链接','摘要']
#调用writer对象的writerow()方法,可以在csv文件里写入一行文字 “标题”和“链接”和"摘要"。
writer.writerow(list2)
for article in list1:
writer.writerow(article) #每次写入一行
csv_file.close()
————————每个人都在抱怨生活不易,可是都在默默为生活打拼————————