一、模拟网站登录
我们已经学会了如何在Network中提取xhr类请求内的数据,接下来要处理更多复杂的问题。首先,在之前的学习中,我们始终没有涉及到登录行为,而登录实际上,很多网站需要登录才能访问到我们所需要的信息。这一节,我们来解决如何登录的问题。 准备好练习网站 ,我们开始这次的学习。我们先手动登录网站,看看登陆之后的样子,账号为kaikeba,密码为kaikeba888。 点击登录并滑动至网页底部: 这里有评价页面。那么这节我们就以在练习网站发表评论为例,讲述如何用python完成登录以及发表评论。 首先,要用爬虫工程师的思维思考问题,打开登录网站之后就要右击检查: 这里记得要勾选preserve log(持续显示请求记录,防止请求记录被刷新)。然后我们进行登录,并记录新增的请求。登陆之后看到请求的变化: 相信大家已经发现了wp-login.php请求 ,因为我们可以看到它的状态码是302(目标暂时性转移),而之前我们爬取请求内容的状态码都是200。 ps:可以试试双击这个请求,看有什么事情发生。 接下来我们查看一下这个请求的headers : 这里的url很短,并没有出现“#”或“?”来连接附带参数,并且请求方式也不是之前我们学到的“GET”,而是“POST”。 实际上,POST和GET一样,都可以带参数,但是POST不会在url里面显示出来。这也正是使用POST请求方式的原因,毕竟直接将账号和密码暴露在url里很不安全。当然,这两者的区别更在于get请求会应用于获取网页数据,比如我们之前学的 requests.get();而post 请求则应用于向网页提交数据,比如提交表单类型数据(如账号密码)。 get 和 post 是两种最常用的请求方式,除此之外,还有其他类型的请求方式,如 head, options 等,但由于这些都不是很常用,这里就不做介绍了。 get 和 post 这两种请求方式解释清楚了,就让我们继续往下看: 之前我们已经介绍过了headers里的许多参数,只有 response headers还没有详细介绍。正如requests headers存储的是浏览器的请求信息,response headers存储的是服务器的响应信息。下面就到了第一个重点内容:cookie。
1.cookie及其用法
cookie其实随处可见。当我们需要经常性登录一个网站又不想每次都输入账号密码时,通常会选择“记住密码”标识的选项: 勾选之后,就可以很长时间不用再次输入密码或者会直接自动登录了。这就是cookie在起作用。 如果我们登录kaikeba账号并勾选“记住我”,服务器就会生成一个cookie和这个账号绑定,接着会告诉浏览器,让浏览器把这个cookie存储到本地电脑,下一次浏览器带着cookie访问时,服务器可以直接识别,不需要再次输入账号和密码。 当然,cookie也有时效性,如果长时间未使用或本地存储的cookie丢失,亦或账号信息有所变动,网页还是需要重新登录,这就是原来的cookie失效了。 下面我们继续查看Form Data: log和pwd 显然是我们的账号和密码,wp-submit是登录的按钮,redirect_to 后面带的链接是我们登录后会跳转到的页面网址,但是testcookie暂时还看不出是什么。 既然关于登录的 参数已经都找到了,我们就先尝试一下向服务器发起登录请求:
import requests
url= 'https://xiaoke.kaikeba.com/example/wordpress/wp-login.php'
headers = {
'User-Agent' :
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
data= {
'log' : 'kaikeba' ,
'pwd' : 'kaikeba888' ,
'wp-submit' : '登录' ,
'redirect_to' : 'https://xiaoke.kaikeba.com/example/wordpress/2019/10/17/%e5%bc%80%e8%af%be%e5%90%a7%e6%97%a0%e6%95%8c%e5%a5%bd%e5%90%83%e7%9a%84%e9%a3%9f%e5%a0%82%e4%b8%80%e5%91%a8%e8%8f%9c%e8%b0%b1/' ,
'testcookie' : '1'
}
log_in= requests. post( url, headers= headers, data= data)
print ( log_in. status_code)
看到这个状态码,意味着服务器接收到并响应了登录请求,我们已经登录成功,接下来我们就开始研究怎么进行评论。 我们要利用程序来发表评论,就要先来发表一次评论,观察请求栏会有什么变化。首先清空请求栏,然后发表一条评论观察: 重新加载出来的请求里,wp-comments-post.php的Form Data的内容已经更新出我们刚刚发表评论的内容了。comment的内容对应我们刚刚发表的评论内容,submit对应发表评论按钮,另外两个参数看不太懂,不过也没关系,它们都是和评论有关的参数,所以直接照抄就好。 细看之下还会发现,wp-comments-post.php的数据并没有藏在 XHR 中,而是放在了Other 里。但常规情况下,大部分网站都会把这样的数据存储在 XHR 里,比如知乎的回答。 现在我们回忆一下发表评论的过程:首先得登录,其次得提取和调用登录的 cookie,然后还需要评论的参数,才能发起评论的请求。 现在,我们就只差提取和调用登录的 cookie了。而cookie是每个requests对象的属性,可以使用requests对象.cookie 的方式查到:
import requests
url= 'https://xiaoke.kaikeba.com/example/wordpress/wp-login.php'
headers = {
'User-Agent' :
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
data= {
'log' : 'kaikeba' ,
'pwd' : 'kaikeba888' ,
'wp-submit' : '登录' ,
'redirect_to' : 'https://xiaoke.kaikeba.com/example/wordpress/2019/10/17/%e5%bc%80%e8%af%be%e5%90%a7%e6%97%a0%e6%95%8c%e5%a5%bd%e5%90%83%e7%9a%84%e9%a3%9f%e5%a0%82%e4%b8%80%e5%91%a8%e8%8f%9c%e8%b0%b1/' ,
'testcookie' : '1'
}
log_in= requests. post( url, headers= headers, data= data)
cookie = log_in. cookies
url_comment = 'https://xiaoke.kaikeba.com/example/wordpress/wp-comments-post.php'
data_comment = {
'comment' : input ( '请输入你想要发表的评论:' ) ,
'submit' : '发表评论' ,
'comment_post_ID' : '35' ,
'comment_parent' : '0'
}
comment = requests. post( url_comment, headers= headers, data= data_comment, cookies= cookie)
print ( comment. status_code)
运行这段代码,我们可以输入“下面我们尝试发表评论”,观察运行结果:
到这里我们已经成功发表了评论。但是项目并没有结束,这个代码还有优化空间。 注:这个网站不能发送重复的评论,大家练习的时候记得换评论内容
2.session及其用法
session,中文意会话。所谓会话,可以理解为我们用浏览器上网,到关闭浏览器的这一过程。 我们这里要学习的session,实际作用是帮助我们记录会话过程中,服务器用来记录特定用户会话的信息。举个例子,如果没有 session,可能会出现这样搞笑的情况:淘宝购物车添加了很多商品,但在想要结算时,却发现购物车空无一物,因为服务器根本没有帮你记录你想买的商品。 session和cookie的关系十分密切,因为cookie中存储着session的编码信息,session中又存储了cookie的信息。
以访问购物网页为例,当浏览器第一次访问时,服务器会返回set cookie的字段给浏览器,浏览器会把cookie保存到本地;等浏览器第二次访问这个购物网页时,就会带着 cookie 去请求,而cookie里带有session的编码信息,服务器立马就能辨认出这个用户,同时返回和这个用户相关的特定编码的 session。尽管存放在本地的cookie会有很多种可能失效,但是服务器里存储的cookie会一直保存session的编码信息,无论是更改密码,还是长时间未登陆等情况,都几乎不会丢失。 正因如此,我们每次重新登录购物网站后,即使cookie存储的内容(如密码)有所变化,只要session的编码信息没变,都还是可以找到保存在购物车中的商品。 session介绍完了,下面就来说如何优化代码。由于cookie和session的关系密切,我们来查查可不可以通过创建一个 session 来处理 cookie : 在 requests 的高级用法里,真有这样的方法~那么下面我们就实战一下:
import requests
session = requests. session( )
url_login= 'https://xiaoke.kaikeba.com/example/wordpress/wp-login.php'
headers = {
'User-Agent' :
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
data_login = {
'log' : input ( '请输入账号:' ) ,
'pwd' : input ( '请输入密码:' ) ,
'wp-submit' : '登录' ,
'redirect_to' : 'https://xiaoke.kaikeba.com/example/wordpress/2019/10/17/%e5%bc%80%e8%af%be%e5%90%a7%e6%97%a0%e6%95%8c%e5%a5%bd%e5%90%83%e7%9a%84%e9%a3%9f%e5%a0%82%e4%b8%80%e5%91%a8%e8%8f%9c%e8%b0%b1/' ,
'testcookie' : '1'
}
session. post( url_login, headers= headers, data= data_login)
url_comment= 'https://xiaoke.kaikeba.com/example/wordpress/wp-comments-post.php'
data_comment = {
'comment' : input ( '请输入你想要发表的评论:' ) ,
'submit' : '发表评论' ,
'comment_post_ID' : '35' ,
'comment_parent' : '0'
}
comment= session. post( url_comment, headers= headers, data= data_comment)
print ( comment)
请大家自己运行,可以看到程序会打印出我们评论的内容的,这也说明调用.post()方法会返回我们评论的内容。 这段代码可以自己提取cookie,也就意味着不需要先登录获取cookies再登录进行评论,可以只登录一次就完成评论。是不是简单了一些?但还不够理想。我们能不能将cookie存在指定位置,然后每次直接自动登录,发表评论呢?
3.cookies的存取及调用
①cookies的存储 下面我们就来探讨如何把cookie保存到指定位置,完成第一次登录后,每一次都进行自动登录。 我们先来看看cookie的内容和类型:
import requests
url= 'https://xiaoke.kaikeba.com/example/wordpress/wp-login.php'
headers = {
'User-Agent' :
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
data= {
'log' : 'kaikeba' ,
'pwd' : 'kaikeba888' ,
'wp-submit' : '登录' ,
'redirect_to' : 'https://xiaoke.kaikeba.com/example/wordpress/2019/10/17/%e5%bc%80%e8%af%be%e5%90%a7%e6%97%a0%e6%95%8c%e5%a5%bd%e5%90%83%e7%9a%84%e9%a3%9f%e5%a0%82%e4%b8%80%e5%91%a8%e8%8f%9c%e8%b0%b1/' ,
'testcookie' : '1'
}
log_in= requests. post( url, headers= headers, data= data)
cookie= log_in. cookies
print ( cookie)
print ( type ( cookie) )
cookie的内容我们看不懂,只是觉得有点像字典。这没有什么影响,但是这个类型有点令人头疼。我们可以将str类型的数据写入.txt文档,但这里的数据类型却是 ‘requests.cookies.RequestsCookieJar’。 如何保存这种类型的数据,相信大家已经回想起之前我们学过的json模块了。或许我们可以先把 cookie 转成字典,然后再通过 json 模块转成字符串。 当然我们想要使用数据的时候还需要先把字符串转化成字典,然后再转化为’RequestsCookieJar’类型。有json模块,字典和str类型的相互转化好说,但是怎么完成’RequestsCookieJar’类型与字典类型的双向转化呢? 这个方法引发了博主的一些思考。我们看代码:
import requests
import json
session= requests. session( )
url= 'https://xiaoke.kaikeba.com/example/wordpress/wp-login.php'
headers = {
'User-Agent' :
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
data = {
'log' : 'kaikeba' ,
'pwd' : 'kaikeba888' ,
'wp-submit' : '登录' ,
'redirect_to' :
'https://xiaoke.kaikeba.com/example/wordpress/2019/10/17/%e5%bc%80%e8%af%be%e5%90%a7%e6%97%a0%e6%95%8c%e5%a5%bd%e5%90%83%e7%9a%84%e9%a3%9f%e5%a0%82%e4%b8%80%e5%91%a8%e8%8f%9c%e8%b0%b1/' ,
'testcookie' : '1'
}
session. post( url, headers= headers, data= data)
cookie_dict= requests. utils. dict_from_cookiejar( session. cookies)
print ( type ( cookie_dict) )
cookie_str = json. dumps( cookie_dict)
print ( type ( cookie_str) )
with open ( 'cookie.txt' , 'w+' ) as f:
f. write( cookie_str)
f. seek( 0 )
print ( f. read( ) )
运行一下,和刚才的结果对比,cookie的文字内容没有变化但是类型已经成功地由’RequestsCookieJar’转化为字典,进而转化为str并被保存起来了。 ②cookies的读取 接下来我们研究这个过程的逆过程。刚刚将cookies转化为字典类型的方法是:
dict_= requests. utils. dict_from_cookiejar( )
那么将字典转化成cookies原有格式的方法也比较类似:
cookies= requests. utils. cookiejar_from_dict( )
接下来,我们将刚保存的txt文档打开并将数据还原成RequestsCookieJar类型:
import requests
import json
with open ( 'cookie.txt' , 'r' ) as f:
con= f. read( )
cookies_dict= json. loads( con)
print ( type ( cookies_dict) )
cookies_jar= requests. utils. cookiejar_from_dict( cookies_dict)
print ( cookies_jar, '\n' , type ( cookies_jar) )
经过检查,输出的内容和其类型和我们最初直接从网页上获取的一致,我们成功完成了cookies的存取。 ③cookies的调用 之前我们已经可以存储和读取cookies了,下面我们学着使用存储的cookies登录网页发表评论。编写代码之前,我们先梳理一下代码执行的过程: (i)尝试读取cookies并登录网站,成功则执行(iii),读取到的cookies过期或没有读取到cookies执行(ii) (ii)进入登录页面进行登录并存储cookies (iii)发表评论 下面编写代码:
import requests, json
session= requests. session( )
headers = {
'User-Agent' :
'Mozilla/5.0 (Windows NT 6.1; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
def cookies_read ( ) :
with open ( 'cookie.txt' , 'r' ) as f:
con= f. read( )
cookies_dict= json. loads( con)
return ( requests. utils. cookiejar_from_dict( cookies_dict) )
def sign_in ( ) :
url= 'https://xiaoke.kaikeba.com/example/wordpress/wp-login.php'
data_login = {
'log' : input ( '请输入你的账号:' ) ,
'pwd' : input ( '请输入你的密码:' ) ,
'wp-submit' : '登录' ,
'redirect_to' :
'https://xiaoke.kaikeba.com/example/wordpress/2019/10/17/%e5%bc%80%e8%af%be%e5%90%a7%e6%97%a0%e6%95%8c%e5%a5%bd%e5%90%83%e7%9a%84%e9%a3%9f%e5%a0%82%e4%b8%80%e5%91%a8%e8%8f%9c%e8%b0%b1/' ,
'testcookie' : '1'
}
session. post( url, headers= headers, data= data_login)
cookie_dict= requests. utils. dict_from_cookiejar( session. cookies)
cookie_str = json. dumps( cookie_dict)
with open ( 'cookie.txt' , 'w' ) as f:
f. write( cookie_str)
def write_message ( ) :
url_comment = 'https://xiaoke.kaikeba.com/example/wordpress/wp-comments-post.php'
data_comment = {
'comment' : input ( '请输入你要发表的评论:' ) ,
'submit' : '发表评论' ,
'comment_post_ID' : '35' ,
'comment_parent' : '0'
}
return ( session. post( url_comment, headers= headers, data= data_comment) )
try :
session. cookies = cookies_read( )
except :
sign_in( )
session. cookies = cookies_read( )
comment= write_message( )
if comment. status_code == 200 :
print ( '成功啦!' )
else :
print ( '存储的cookies过期,需要重新登录!' )
sign_in( )
session. cookies = cookies_read( )
comment = write_message( )
这段代码需要进行三次测试,首先直接运行,看能否发表评论;然后更改cookie.txt文档内容(注意要保证字典的完整性,不能只删除某一个键或值等,否则会报错)再运行,运行之后观察cookie.txt文档内容是否和修改之前一样;然后删除这个文档再运行一次。三次都成功运行则代码没有问题。当然,想要清楚地看到代码运行过程的小伙伴可以使用debug进行单步运行: 这个样子说明我们已经运行成功了。这才是我们优化代码的最终版。 我们已经发过的评论就不能再发了,小伙伴们练习的时候记得换评论内容。
二、用程序指挥浏览器
上一节我们已经介绍了网页登录,但是有些网站的登录验证码很难破解,还有的网站对URL的加密逻辑很复杂,像之前爬过的五月天歌曲列表,URL的参数变量比较难找。通常这样的情况下,想要攻破反爬技术会有些难度。下面我们就来介绍一款新的武器:
1.初识selenium
selenium是一个强大的 Python 库,使用selenium后可以看到浏览器自动打开、输入、点击等操作,很像我们在操作浏览器。 我们编写的程序遇到验证码很复杂的网站时,selenium 可以使用延时函数暂停运行,进而让人工介入,手动输入验证码,然后把剩下的操作交给程序去完成。 同时,对于那些交互复杂、加密复杂的网站,requests.get()函数爬取不到全部的网页代码,只能在Network里寻找对应请求。归根结底,这是因为没有真正打开网站的时候,很多请求是不会由浏览器向服务器发起。但selenium可以真正的打开一个网站,向服务器发出并完成所有请求后,将它们一起组成开发者工具的Elements中所展示的样子。也因此使用selenium爬取网站的内容时不必寻找请求源,而是可以直接在Elements中爬取内容,爬动态网页如爬静态网页一样简单。 当然,selenium也有自己的缺点。由于要真实地运行本地浏览器,打开浏览器以及等待网渲染完成需要一些时间,selenium的工作不可避免地牺牲了爬取速度和更多资源。不过,肯定还是要比手动的操作快很多。
2.selenium的使用
以上说了很多文字内容,多少有些难以理解,因此我们接下来一起学习selenium的使用。那么,和其它所有Python库一样,需要安装selenium。当然只有标准库还不够,我们想要让代码操作浏览器还需要安装驱动 。 安装完成之后,先运行一下下面的代码,体会一下selenium的神奇之处:
from selenium import webdriver
import time
driver = webdriver. Chrome( )
driver. get( 'https://xiaoke.kaikeba.com/example/X-Man/' )
time. sleep( 2 )
teacher = driver. find_element_by_id( 'teacher' )
teacher. send_keys( '1' )
assistant = driver. find_element_by_name( 'assist' )
assistant. send_keys( '2' )
time. sleep( 2 )
button = driver. find_element_by_tag_name( 'button' )
time. sleep( 1 )
button. click( )
time. sleep( 5 )
driver. close( )
首先我们看到的是【你好,X战警】几个大字,一秒之后,它会自动跳转到一个新的页面,请你输入最喜欢的老师和助教,点击提交之后,就能看到一个关于爬虫的课程表。仔细看会发现,在这个过程中,网页URL一直没有变化。可见这是个动态网页。 代码运行完之后,我们手动登录刚刚的网站 ,执行一下同样的操作试试看。 下面我们开始代码的讲解:
from selenium import webdriver
driver = webdriver. Chrome( )
driver 是实例化的浏览器,后面也会经常出现,因为我们要控制这个实例化的浏览器为我们做一些事情。
driver. get( 'https://xiaoke.kaikeba.com/example/X-Man/' )
time. sleep( 3 )
driver. close( )
driver.get(URL) 是 webdriver 的一个方法可以打开指定的网页,使用的浏览器,就是刚刚实例化的浏览器。这个网页被打开后,网页的全部数据就会加载到浏览器中,也就都可以被我们获取到了。driver.close() 是关闭浏览器驱动,每次打开浏览器后都需要关闭,否则会占用系统内存。当然,打开的浏览器也可以手动关闭,不过浏览器关闭之后代码就不能获取网页的信息了。因此想要手动关闭的小伙伴一定要记得在代码运行完后进行关闭。
3.解析网站,提取数据
与BeautifulSoup库类似,selenium 库同样也具备解析数据、提取数据的能力。他们的底层原理一致,只是一些细节和语法上有所出入。首先明显的一个不同就是selenium所解析提取的,是Elements中的所有数据,而BeautifulSoup所解析的则只是Network中第 0 个请求的响应。 下面就让我们通过使用 selenium 来提取【你好,X战警】网页中, 元素的内容,来演示 selenium 是如何解析与提取数据的:
from selenium import webdriver
import time
driver= webdriver. Chrome( )
driver. get( 'https://xiaoke.kaikeba.com/example/X-Man/' )
time. sleep( 3 )
label= driver. find_element_by_tag_name( 'label' )
print ( label. text)
print ( type ( label) )
driver. close( )
从这个结果中可以看出想要提取到的label标签名对应的第一个元素的文字内容。其实不难发现,解析代码是由driver实例打开网页时自动完成的,提取数据则是driver实例的一个方法:.find_element_by_tag_name()。 当然,webdriver自带的查找方法有很多,并不只局限于依据标签提取内容,还有非常直截了当的方法:
方法 作用 found_element_by_id() 通过元素id查找 found_element_by_class_name() 通过元素class查找 found_element_by_tag_name() 通过元素标签名查找 found_element_by_name() 通过元素内容查找 found_element_by_partial_link_text() 通过文本的内容查找标签中含有的超链接 found_element_by_link_text() 通过文本的部分内容查找标签中含有的超链接
下面我们进行举例说明:
driver. find_element_by_tag_name( 'h1' )
driver. find_element_by_class_name( 'title' )
driver. find_element_by_id( 'title' )
driver. find_element_by_name( 'hello' )
driver. find_element_by_link_text( '你好,X战警' )
driver. find_element_by_partial_link_text( '你好' )
以上方法均只能提取第一个元素,需要提取全部元素时,将“element”改为“elements”即可。提取出的元素类型为:WebElement,这个类型与Tag类似,也有一个属性为.text,用于把提取出的元素用字符串格式显示;并且它也有一个方法.get_attribute(),可以通过属性名提取属性的值。
WebElement Tag 作用 .text .text 获取文字内容 .get_attribute() [’’] 依据属性名获取属性值
举个例子介绍这个属性: 假如我们想找到type对应的值:
from selenium import webdriver
import time
driver = webdriver. Chrome( )
driver. get( 'https://xiaoke.kaikeba.com/example/X-Man/' )
time. sleep( 2 )
label = driver. find_element_by_class_name( 'form-teacher' )
print ( type ( label) )
print ( label. get_attribute( 'type' ) )
driver. close( )
那么,有的小伙伴可能会说,我已经习惯使用.find()和.find_all()来提取内容了,selenium提取的内容可以用BeautifulSoup解析吗? 当然可以了,下面我们就介绍一下selenium如何与BeautifulSoup愉快合作。webdriver有一个方法page_source,可以获取到渲染完整的网页代码,我们只需要使用该方法将获取到的网页源代码交给BeautifulSoup来解析就好了,上代码:
from selenium import webdriver
import time
from bs4 import BeautifulSoup as bs
driver = webdriver. Chrome( )
driver. get( 'https://xiaoke.kaikeba.com/example/X-Man/' )
time. sleep( 2 )
p= driver. page_source
page_source= bs( p, 'html.parser' )
want = page_source. find_all( 'span' ) [ 1 ] . find( 'input' )
print ( type ( want) )
print ( want)
print ( want[ 'type' ] )
driver. close( )
很好,我们已经学会了selenium与BeautifulSoup合作了,那么接下来给大家重点解释一下这句代码:
want = page_source. find_all( 'span' ) [ 1 ] . find( 'input' )
首先,使用.find_all()找到的内容为一个列表,里面每个元素的类型均为Tag。 从这张图中不难发现,我们想找的标签在第二个span标签下,因此我们需要使用.find_all()找到所有的span标签,并锁定这个列表中第二个(下标为1的)元素,然后再寻找input标签。当然,这里有很多更简单的方法找到我们想要的内容,故意选择这种方法只是和大家分享一下这种用法。
4.文本输入与模拟点击
上面我们已经把提取内容讲解的十分清楚了,接下来继续讲解如何利用代码向网页输入内容并进行模拟点击。 现给大家介绍我们会用到的方法:
方法 作用 .clear() 清除元素的所有内容 .send_keys() 模拟键盘输入,参数为需要输入的内容 .click() 模拟点击元素
我们可以通过之前介绍的方法找到需要填写内容的位置:
找到后我们就开始实践:
from selenium import webdriver
import time
driver = webdriver. Chrome( )
driver. get( 'https://xiaoke.kaikeba.com/example/X-Man/' )
time. sleep( 2 )
teacher = driver. find_element_by_id( 'teacher' )
teacher. send_keys( '1' )
time. sleep( 1 )
teacher. clear( )
time. sleep( 1 )
teacher. send_keys( 'k老师' )
time. sleep( 1 )
assistant = driver. find_element_by_name( 'assist' )
assistant. send_keys( '学姐' )
time. sleep( 1 )
button = driver. find_element_by_tag_name( 'button' )
button. click( )
time. sleep( 2 )
driver. close( )
这样一来,这节理要讲的最后的一部分内容已经包含在代码里了,要重点看注释。在学习输入、清除内容和模拟点击的同时,也顺带复习一下之前学的东西吧,然后我们进行下一节的学习。
三、让爬虫学会定时汇报
有时候,我们编写爬虫的目的不是一次性爬取内容,而是需要爬虫定期汇报,比如每天早晨上班之前为我们发送天气预告。关于定时功能,python自带的标准库——time和datetime都可以完成。但是,这两个基本库只有最原始基础的功能,实现起来的操作逻辑比较复杂,因此选择使用第三方库schedule是实现,其代码也比较简单。
1.schedule的使用方法
首先,先给大家提供schedule的官方文档 介绍。看不懂也没有关系,下面我们一起学习这个库的一些基本用法:
import schedule
import time
def job ( ) :
print ( "Working in progress..." )
schedule. every( 5 ) . seconds. do( job)
schedule. every( 10 ) . minutes. do( job)
schedule. every( ) . hour. do( job)
schedule. every( ) . day. at( "10:30" ) . do( job)
schedule. every( ) . monday. do( job)
schedule. every( ) . wednesday. at( "13:15" ) . do( job)
while True :
schedule. run_pending( )
time. sleep( 1 )
实战中,我们需要自己写好job()函数,然后确定部署时间,需要注意的是,如果部署情况为1秒执行一次job()函数,而检查部署为3秒检查一次,那么运行结果是3秒执行一次job()函数,也就是取这两者的最小公倍数。 下面举个例子:
import schedule
import time
def job ( ) :
print ( "Working in progress..." )
schedule. every( 1 ) . seconds. do( job)
while True :
schedule. run_pending( )
time. sleep( 1 )
运行的结果是每隔一秒,计算机输出一次“Working in progress…”。
2.schedule的实战应用
上面我们已经学会了schedule的基本使用方法,下面我们就来做一个实战,让我们的的爬虫每天以邮件方式向我们汇报明天天气。
i.爬取明天的天气信息
首先先找到中国天气网 ,选择自己所在的城市。这里以北京为例,选择七天的天气预报: 还是用老方法找到源代码中气温的位置(这个网页的HTML源代码就包含了全部的网页的全部信息。要养成动手实践之前要注意先查看一下的习惯。)找到位置以后,我们确定要爬取的内容,这里仅选取气温和天气。 下面先编写这部分代码: 我们先看看全部代码,验证一下有没有问题:
import requests
from bs4 import BeautifulSoup as bs
url= 'http://www.weather.com.cn/weather/101010100.shtml'
headers= { 'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36' }
res= requests. get( url, headers= headers, verify= False )
print ( res. text)
运行这段代码,可以很明显看出,提取到的内容是满足我们的需求的,但是却出现了很多乱码: 出现这种问题一定是编码解码的过程出现了问题,运用之前介绍的知识可以解决这个问题,但是这里给大家介绍一种新的方法,首先我们看到网页源代码的最开始部分: 这里是这个网站所用到的编码方式。那么我们只需要在给res赋值之后,设置res的编码方式为utf-8即可。代码改进如下:
res= requests. get( url, headers= headers, verify= False )
res. encoding = 'utf-8'
下面我们就正式爬取天气内容:
import requests
from bs4 import BeautifulSoup as bs
url= 'http://www.weather.com.cn/weather/101010100.shtml'
headers= { 'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36' }
res= requests. get( url, headers= headers, verify= False )
res. encoding = 'utf-8'
analysis= page_source= bs( res. text, 'html.parser' )
tem= analysis. find_all( class_= 'tem' ) [ 1 ]
print ( '明天气温为:' + tem. text. replace( '\n' , '' ) , end= ',' )
weather= analysis. find_all( 'p' , class_= "wea" )
print ( '天气为:' + weather[ 1 ] [ 'title' ] . replace( '\n' , '' ) + '。' )
ii.使用QQ邮箱发送邮件
如何使用python发送邮件在之前的文章中有过具体介绍,这里不做赘述,直接给出代码,忘记的小伙伴可以去复习 。
import smtplib
from email. mime. text import MIMEText
from email. header import Header
from_addr = input ( '请输入发件人的邮箱: ' )
password = input ( '请输入发件人的 QQ 邮箱授权码: ' )
to_addrs= input ( '请输入收件人邮箱:' )
text = '将天气信息写入邮件正文内容'
msg = MIMEText( text, 'plain' , 'utf-8' )
msg[ 'From' ] = Header( from_addr)
msg[ 'To' ] = Header( to_addrs)
msg[ 'Subject' ] = Header( '明日天气预报' )
try :
server = smtplib. SMTP_SSL( )
server. connect( 'smtp.qq.com' , 465 )
server. login( from_addr, password)
server. sendmail( from_addr, to_addrs, msg. as_string( ) )
print ( '发送成功' )
server. quit( )
except :
print ( '发送失败' )
iii.设置定时信息
应用之前讲的知识设置发送时间,当然,测试运行时可以把发送时间设置成几秒:
import schedule
import time
def send ( ) :
return 0
schedule. every( ) . day. at( "22:30" ) . do( send)
while True :
schedule. run_pending( )
time. sleep( 1 )
iv.代码整合
各部分的功能都实现之后,我们将代码整合:
import requests
from bs4 import BeautifulSoup as bs
import smtplib
from email. mime. text import MIMEText
from email. header import Header
import schedule
import time
url= 'http://www.weather.com.cn/weather/101010100.shtml'
headers= { 'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36' }
from_addr = input ( '请输入发件人的邮箱: ' )
password = input ( '请输入发件人的 QQ 邮箱授权码: ' )
to_addrs = input ( '请输入收件人邮箱:' )
def find_weather ( ) :
res = requests. get( url, headers= headers, verify= False )
res. encoding = 'utf-8'
analysis = page_source = bs( res. text, 'html.parser' )
tem = analysis. find_all( class_= 'tem' ) [ 1 ]
weather = analysis. find_all( 'p' , class_= "wea" )
return '明天气温为:' + tem. text. replace( '\n' , '' ) + ',' + '天气为:' + weather[ 1 ] [ 'title' ] . replace( '\n' , '' ) + '。'
def send_email ( ) :
weather= find_weather( )
text = weather
msg = MIMEText( text, 'plain' , 'utf-8' )
msg[ 'From' ] = Header( from_addr)
msg[ 'To' ] = Header( to_addrs)
msg[ 'Subject' ] = Header( '明日天气预报' )
try :
server = smtplib. SMTP_SSL( )
server. connect( 'smtp.qq.com' , 465 )
server. login( from_addr, password)
server. sendmail( from_addr, to_addrs, msg. as_string( ) )
print ( '发送成功' )
server. quit( )
except :
print ( '发送失败' )
schedule. every( ) . day. at( "22:30" ) . do( send_email)
while True :
schedule. run_pending( )
time. sleep( 1 )
到此为止,我们的目的已经完成了。感兴趣的小伙伴可以自己运行试试。这节的内容就到这里了,我们下篇文章见~