目录

1.webbrowser模块

1.1 弄清楚URL

1.2 处理命令行参数

2. 用requests模块从Web下载文件

2.1 用requests.get()函数下载一个网页

2.2 检查错误

2.3 将下载的文件保存到硬盘

3. 用BeautifulSoup模块解析HTML

3.1 从HTML创建一个BeautifulSoup对象

4. 小项目《1》:查找一个话题并针对每个结果打开浏览器界面

5. 小项目《2》下载所有XKCD漫画

6. 自动化网页任务——selenium模块

6.1 驱动Chrome浏览器

6.2 在Web页面中寻找元素

6.3 点击页面

6.4 填写并提交表单(模拟自动登录QQ空间)

6.5 发送特殊键

6.6 点击浏览器按钮

关于selenium 的更多信息

小结


“Web抓取”是一个术语,即利用程序下载并处理来自Web的内容。例如,Google运行了许多Web抓取程序,对网页进行搜索,实现它的搜索引擎。在本章中,将学习几个模块,让在Python中抓取网页变得很容易。

webbrowser:是Python自带的,打开浏览器获取指定页面(一般会打开你Windows电脑的默认浏览器)。

requests:从因特网上下载文件和网页。

Beautiful Soup:解析HTML,即网页编写的格式。

selenium:启动并控制一个Web浏览器,selenium能够填写表单,并模拟鼠标在这个浏览器中点击。

1.webbrowser模块

webbrowser模块的open()功能可以启动一个新的浏览器到指定的URL。在交互式shell中输入以下内容:

>>> import webbrowser
>>> webbrowser.open('http://inventwithpython.com/')

Web浏览器选项卡将打开URL http://inventwithpython.com/。这是webbrowser模块唯一可以做的事情。即便如此,该open()功能确实使一些有趣的事情成为可能。例如,将街道地址复制到剪贴板并在Google地图上显示它的地图是很繁琐的。通过编写一个简单的脚本,您可以使用剪贴板的内容在浏览器中自动启动地图,从而完成此任务。这样,您只需将地址复制到剪贴板并运行脚本,即可为您加载地图。

这是你的程序所做的:

  • 从命令行参数或剪贴板获取街道地址。

  • 打开Web浏览器,访问Google地图页面以获取地址。

这意味着您的代码需要执行以下操作:

  • 从中读取命令行参数sys.argv

  • 阅读剪贴板内容。

  • 调用该webbrowser.open()函数以打开Web浏览器。

打开一个新的文件编辑器窗口并将其另存为mapIt.py

1.1 弄清楚URL

首先你需要弄清楚,对于指定的街道,要使用怎样的URL。在浏览器中打开谷歌地图中国官网的地址:http://www.google.cn/maps ,查找一个地址,例如“杭州市”,地址栏中的URL看起来像这样:http://www.google.cn/maps/place/浙江省杭州市/@30.2610923,119.8917005,10z/data=!3m1!4b1!4m5!3m4!1s0x344bb629439aaa99:0xa7bfd183824de83a!8m2!3d30.274084!4d120.15507

地址就在URL中,但还有许多的附加文本。网站常常在URL中添加额外的数据,帮助追踪者访问或定制网站。但如果你尝试使用http://www.google.cn/maps/place/浙江省杭州市,会返现仍然可以代打正确的页面。所以你的程序可以设置为打开一个浏览器,访问http://www.google.cn/maps/place/your_address ,其中 your_address是想查看地图的地址。

1.2 处理命令行参数

在程序的#!行之后,您需要导入webbrowser模块以启动浏览器并导入sys模块以读取潜在的命令行参数。该sys.argv变量存储程序的文件名和命令行参数的列表。如果此列表中不仅包含文件名,则len(sys.argv)计算结果为大于的整数1,这意味着确实提供了命令行参数。

命令行参数通常用空格分隔,但在这种情况下,您希望将所有参数解释为单个字符串。由于sys.argv是字符串列表,您可以将其传递给join()方法,该方法返回单个字符串值。你不希望这个字符串中出现的程序名称,所以sys.argv你应该传递sys.argv[1:]而不是sys.argv[0:],去除数组的第一个元素。此表达式求值的最终字符串存储在address变量中。

完整的程序加调试log看起来像这样:

#!/usr/bin/python3 # -*- coding: utf-8 -*#mapit.py launches a map in browser using an address from the command line or clipboard.#currend directory: Learning\PYTHON\python-auto\100-days\webimport webbrowser, sys, pyperclip
import requests
import traceback
import socket
#设置socket 寻址超时时间
socket.setdefaulttimeout(15)
#导入日志模块
import logging
#LOG_FORMAT = "[ %(asctime)s %(name)s %(levelname)s %(pathname)s ] %(message)s "#配置输出日志格式
LOG_FORMAT = "%(message)s"
DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
LOG_PATH = None #os.path.join(os.getcwd(),'debugging.log')
logging.basicConfig(level=logging.DEBUG,format=LOG_FORMAT,datefmt = DATE_FORMAT ,filename=LOG_PATH #有了filename参数就不会直接输出显示到控制台,而是直接写入文件)def open_map():if len(sys.argv) > 1:#Get address from command lineaddress = ' '.join(sys.argv[1:]) #以空格' ' 分割else:#Get address from clipboardaddress = pyperclip.paste()logging.debug('try to open google map from browser@address in %s.' %(address))webbrowser.open('http://www.google.cn/maps/place/' + address)if __name__ == '__main__':open_map()

运行:

python mapit.py 杭州市

2. 用requests模块从Web下载文件

requests 模块让你很容易从Web下载文件,不必担心一些诸如网络错误,链接问题和数据压缩等问题。requests 模块不是Python自带的,所以需要先安装,命令行窗户运行 pip install requests 。

2.1 用requests.get()函数下载一个网页

requests.get()函数接受一个要下载的URL字符串。通过在requests.get()的返回值上调用type(),你可以看到他返回一个Response对象,其中包含了Web服务器对你的请求作出的响应。通过检查Response对象的status_code属性,你可以了解对这个网页的请求是否成功。如果该值等于requests.codes.ok那么表示一切顺利(顺便说一下,HTTP协议中"OK"的状态码是200。你可能已经熟悉04状态码,它表示"没找到")。如果请求成功,下载的页面就作为一个字符串,保存在Response对象的text变量中,这个变量保存了包含一整个文档的内辞工。可以调用len()来获取该字符串的长度。requests.get()传入参数是URL,可以用以下两个,为古登堡计划公益项目的电子图书下载地址:

url = 'https://www.gutenberg.org/files/60176/60176-0.txt'

url = 'https://www.gutenberg.org/cache/epub/1112/pg1112.txt'

#! /usr/bin/python # -*- coding: utf-8 -*import requests
import logging
import os
#LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
LOG_FORMAT = "%(message)s " #简要的格式,只包含输入文本
DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
LOG_PATH =  None #os.path.join(os.getcwd(),'FTP_LOGIN.log')
logging.basicConfig(level=logging.DEBUG,format=LOG_FORMAT,datefmt = DATE_FORMAT ,filemode='w', #覆盖之前的记录 'a'是追加filename=LOG_PATH #有了filename参数就不会直接输出显示到控制台,而是直接写入文件)def web_request(url):#下载TXT文本res = requests.get(url)try:logging.debug('try to get %s ...' %(url))res.raise_for_status()except Exception as exc:print('There was some error in requests.get:' %(exc))return False#走到这里说明下载成功#将下载的文件保存到本地logging.debug('get book success!, try to save be Unicode file now...')save_path = os.path.join(os.getcwd(), 'Dancers_in_the_Dark.txt')#save_path = os.path.join(os.getcwd(), 'THE_TRAGEDY_OF_ROMEO_AND_JULIET.txt')with open(save_path, 'wb') as playFile: #用 with open as 打开不需要调用close(),以写或覆盖的模式打开二进制文件for words in res.iter_content(100000):wt_len = playFile.write(words)logging.debug('%d bytes writed...' %(wt_len))logging.debug('Done!, please open %s to check completting!' %(save_path))def call_request():url = 'https://www.gutenberg.org/files/60176/60176-0.txt'#url = 'https://www.gutenberg.org/cache/epub/1112/pg1112.txt'web_request(url)if '__main__' == __name__:call_request()

2.2 检查错误

Response对象有一个status_code属性,可以检查它是否等于requests.code.ok,了解是否下载成功,检查成功有一种简单的方法,如上述代码中所示,在Response对象上调用raise_for_status()方法。如果下载文件出错,将抛出异常。raise_for_status()方法是一种很好的方式,确保程序在下载失败时停止。这是一件好事,你希望程序在发生未预期的错误时,及时停止。如果下载失败对于程序来说不够严重,可以用try和expect语句将raise_for_status()代码行包装起来,正确处理这一错误,不至于让程序直接崩溃。

2.3 将下载的文件保存到硬盘

将从Web下载的文件写入硬盘,可以用标准的open()函数和write()方法,但是稍有不同,这里打开文件时最好用“写二进制”模式打开该文件, 即便你下载的文件时纯文本的,你也需要写入二进制数据,而不是文本数据,目的是为了保存该文本的“Unicode编码”。百科:Unicode是一个编码方案,Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode 编码共有三种具体实现,分别为utf-8,utf-16,utf-32,其中utf-8占用一到四个字节,utf-16占用二或四个字节,utf-32占用四个字节。目前Unicode 码在全球范围的信息交换领域均有广泛的应用。

打开文件的方式:

with open(save_path, 'wb') as playFile:

下面是下载和保存文件的完整过程:

  1. 调用requests.get()下载文件。

  2. 调用open()'wb'以写入二进制模式创建一个新文件。

  3. 循环遍历Response对象的iter_content()方法。

  4. 调用write()将每次迭代内容写入文件。

  5. 调用close()以关闭文件。

3. 用BeautifulSoup模块解析HTML

Beautiful Soup是一个用于从HTML页面提取信息的模块(为此目的,它比正则表达式要好得多)。该BeautifulSoup模块的名称是bs4(对于Beautiful Soup,版本4)。要安装它,您将需要从命令行运行pip install beautifulsoup4。(有关安装第三方模块的说明,请beautifulsoup4参阅附录A。)虽然是用于安装的名称,但要导入要导入的Beautiful Soup import bs4。

对于本章,Beautiful Soup示例将解析硬盘驱动器上的HTML文件(即,分析并标识其中的各个部分)。在IDLE中打开一个新的文件编辑器窗口,输入以下内容,并将其另存为example.html。或者,从http://nostarch.com/automatestuff/下载。

<!-- This is the example.html example file. --><html><head><title>The Website Title</title></head>
<body>
<p>Download my <strong>Python</strong> book from <a href="http://
inventwithpython.com">my website</a>.</p>
<p class="slogan">Learn Python the easy way!</p>
<p>By <span id="author">Al Sweigart</span></p>
</body></html>

3.1 从HTML创建一个BeautifulSoup对象

bs4.BeautifulSoup()需要使用包含将要解析的HTML的字符串来调用该函数。该bs4.BeautifulSoup()函数返回的是一个BeautifulSoup对象。当您的计算机连接到Internet时,使用bs4模块打开html文件,当然也可以直接下载html文件进行解析,先了解以下几个选择器的使用方式:

表3-1 CSS选择器的例子

传递给select()方法的选择器 将匹配的结果
soup.select('div') 所有名为<div>的元素
soup.select('#author') 带有id属性为author的元素
soup.select('.notice') 所有使用CSS class属性名为notice的元素
soup.select('div span') 所有在<div>元素之内的<span>元素
soup.select('div > span') 所有直接在<div>元素之内的<span>元素,中间没有其他元素
soup.select('input[name]') 所有名为<input>,并有一个name属性,其值无所谓的元素
soup.select('input[type="button"]') 所有名为<input>,并有一个type属性,其值为 button的元素

现在,我们打开之前的example.html并使用bs4模块对其进行解析

#! /usr/bin/python # -*- coding: utf-8 -*import requests
import logging
import os, sys, bs4
#LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
LOG_FORMAT = "%(message)s " #简要的格式,只包含输入文本
DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
LOG_PATH =  None #os.path.join(os.getcwd(),'FTP_LOGIN.log')
logging.basicConfig(level=logging.DEBUG,format=LOG_FORMAT,datefmt = DATE_FORMAT ,filemode='w', #覆盖之前的记录 'a'是追加filename=LOG_PATH #有了filename参数就不会直接输出显示到控制台,而是直接写入文件)def get_html_file():url = 'http://nostarch.com'res = requests.get(url)  #一般情况下访问不了try:logging.debug('try to get %s ...' %(url))res.raise_for_status()except Exception as exc:logging.debug('There was some error in requests.get from web:%s.:' %(exc, url))return False#examsoup = bs4.BeautifulSoup(res.text)def phrase_html_file():with open('example.html') as  examhtml:examsoup = bs4.BeautifulSoup(examhtml.read(), features="html.parser")  #如果不指定features,在不同系统可能使用不同的解析器,此行造成以下告警内容,,提示十分友好"""search.py:30: UserWarning: No parser was explicitly specified,so I'm using the best available HTML parser for this system ("html.parser"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.The code that caused this warning is on line 30 of the file search.py. To get rid of this warning, pass the additional argument 'features="html.parser"' to the BeautifulSoup constructor."""elem_author = examsoup.select('#author')print('type(author) = %s' %(type(elem_author)) )print('len(elem_author) = %d' %(len(elem_author)) )for i in range(len(elem_author)):print('type(elem_author[%d]) = %s' %(i, type(elem_author[i])) )print('str(elem_author[%d]) = %s' %(i, str(elem_author[i])) )print('elem_author[%d].getText() = %s' %(i, elem_author[i].getText() ) )print('elem_author[%d].attrs = %s' %(i, elem_author[i].attrs) )if __name__ == '__main__':phrase_html_file()

输出结果:

type(author) = <class 'list'>
len(elem_author) = 1
type(elem_author[0]) = <class 'bs4.element.Tag'>
str(elem_author[0]) = <span id="author">Al Sweigart</span>
elem_author[0].getText() = Al Sweigart
elem_author[0].attrs = {'id': 'author'}

这段代码将带有“id = author”的元素,从式例HTML中找出来,我们使用sleect('#author')返回一个列表,其中包含带有"id = author"的元素。我们将这个Tag对象的列表保存在变量elem_author中,len(elem_author)方法告诉我们列表中只有一个Tag对象,只有一次匹配。在该元素上调用getText()方法,返回该元素的文本,或内部的HTML。一个元素的文本是在开始和结束标签之间的内容:在这个例子中,就是'AI Sweigart'。将该元素传递给str将返回一个字符串,其中包含开始和结束标签,以及该元素的文本。最后,attrs给了我们一个字典,包含钙元素的属性'id'及属性的值'author'。

下面再从例子中找出元素<p>:

#! /usr/bin/python # -*- coding: utf-8 -*import requests
import logging
import os, sys, bs4
#LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
LOG_FORMAT = "%(message)s " #简要的格式,只包含输入文本
DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
LOG_PATH =  None #os.path.join(os.getcwd(),'FTP_LOGIN.log')
logging.basicConfig(level=logging.DEBUG,format=LOG_FORMAT,datefmt = DATE_FORMAT ,filemode='w', #覆盖之前的记录 'a'是追加filename=LOG_PATH #有了filename参数就不会直接输出显示到控制台,而是直接写入文件)def get_html_file():url = 'http://nostarch.com'res = requests.get(url)  #一般情况下访问不了try:logging.debug('try to get %s ...' %(url))res.raise_for_status()except Exception as exc:logging.debug('There was some error in requests.get from web:%s.:' %(exc, url))return False#examsoup = bs4.BeautifulSoup(res.text)def phrase_html_file():with open('example.html') as  examhtml:examsoup = bs4.BeautifulSoup(examhtml.read(), features="html.parser")  #如果不指定features,在不同系统可能使用不同的解析器,此行造成以下告警内容,,提示十分友好"""search.py:30: UserWarning: No parser was explicitly specified,so I'm using the best available HTML parser for this system ("html.parser"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.The code that caused this warning is on line 30 of the file search.py. To get rid of this warning, pass the additional argument 'features="html.parser"' to the BeautifulSoup constructor."""print('NOW SEARCH THE ELEMENT NAME AS <p>:-----------------------------')print("""<!-- This is the example.html example file. --><html><head><title>The Website Title</title></head><body><p>Download my <strong>Python</strong> book from <a href="http://inventwithpython.com">my website</a>.</p><p class="slogan">Learn Python the easy way!</p><p>By <span id="author">Al Sweigart</span></p></body></html>""")""" 找出<p>元素 """elem_p = examsoup.select('p')print('type(elem_p) = %s' %(type(elem_p)) )print('len(elem_p) = %d' %(len(elem_p)) )for i in range(len(elem_p)):print('type(elem_p[%d]) = %s' %(i, type(elem_p[i])) )print('str(elem_p[%d]) = %s' %(i, str(elem_p[i])) )print('elem_p[%d].getText() = %s' %(i, elem_p[i].getText() ) )print('elem_p[%d].attrs = %s' %(i, elem_p[i].attrs) )if __name__ == '__main__':phrase_html_file()

结果:

NOW SEARCH THE ELEMENT NAME AS <p>:-----------------------------

<!-- This is the example.html example file. -->

<html><head><title>The Website Title</title></head>
            <body>
            <p>Download my <strong>Python</strong> book from <a href="http://
            inventwithpython.com">my website</a>.</p>
            <p class="slogan">Learn Python the easy way!</p>
            <p>By <span id="author">Al Sweigart</span></p>
            </body></html>

type(elem_p) = <class 'list'>
len(elem_p) = 3
type(elem_p[0]) = <class 'bs4.element.Tag'>
str(elem_p[0]) = <p>Download my <strong>Python</strong> book from <a href="http://
inventwithpython.com">my website</a>.</p>
elem_p[0].getText() = Download my Python book from my website.
elem_p[0].attrs = {}
type(elem_p[1]) = <class 'bs4.element.Tag'>
str(elem_p[1]) = <p class="slogan">Learn Python the easy way!</p>
elem_p[1].getText() = Learn Python the easy way!
elem_p[1].attrs = {'class': ['slogan']}
type(elem_p[2]) = <class 'bs4.element.Tag'>
str(elem_p[2]) = <p>By <span id="author">Al Sweigart</span></p>
elem_p[2].getText() = By Al Sweigart
elem_p[2].attrs = {}

这一次,select()给我们返回一个列表,包含3个元素,即有3次匹配,我们将它保存在elem_p中,在elem_p[0],elem_p[1],elem_p[2]上使用str(),将每个元素显示为一个字符串,在每个元素上使用getText()显示它的文本。

注意,属性并不是每个元素都存在的,只有在存在属性的元素中才能得到属性。

通过元素属性也可以获取数据

Tag对象的get()方法让我们很容易从元素中获取属性值,向该方法传入一个属性名称的字符串,它将返回该属性的值。

例如:

>>> spanElem = soup.select('span')[0]
>>> spanElem.get('id')
'author'

4. 小项目《1》:查找一个话题并针对每个结果打开浏览器界面

import sys, webbrowser, requests, bs4
# 从参数列表中获取关键词
keywords = '%20'.join(sys.argv[1:])# 进行百度搜索并下载搜索页面
fakeua = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36"}
searchPage = requests.get('https://www.baidu.com/s?wd=' + keywords, headers = fakeua)
searchPage.raise_for_status()    # 如果失败就抛出异常# 得到前5个搜索结果的链接
searchSoup = bs4.BeautifulSoup(searchPage.text, features="html.parser")
elements = searchSoup.select('.t a')# 在浏览器中打开这些连接
for i in range(min(5, len(elements))):webbrowser.open(elements[i].get('href'))

获取命令行参数使用sys.argv[1:]

5. 小项目《2》下载所有XKCD漫画

博客和其他定期更新的网站通常会在首页上显示最新的帖子,并在页面上的“上一页”按钮将您带到上一页。然后,该帖子也将具有“上一个”按钮,依此类推,从而创建从最新页面到站点上第一篇帖子的跟踪。如果您想在不在线时阅读网站内容的副本,则可以手动浏览每个页面并保存每个页面。但这是一件很无聊的工作,所以让我们编写一个程序来代替它。

XKCD是一个流行的极客漫画网站,其网站符合此结构(请参看图5-1)。位于http://xkcd.com/的首页上有一个“上一页”按钮,可引导用户浏览以前的漫画。手动下载每个漫画将花费很多时间,但是您可以编写脚本在几分钟内完成此操作。

图5-1

这是您的程序的作用:

  • 加载XKCD主页。

  • 将漫画图像保存在该页面上。

  • 跟随上一个漫画链接。

  • 重复直到到达第一个漫画。

这意味着您的代码将需要执行以下操作:

  • 下载带有requests模块的页面。

  • 使用Beautiful Soup查找页面的漫画图像的URL。

  • 使用下载并保存漫画图像到硬盘iter_content()

  • 找到上一个漫画链接的URL,然后重复。

打开一个新的文件编辑器窗口,并将其另存为downloadXkcd.py

首先你需要找到漫画在网页中的准确位置元素:如下图5-2所示

图5-2

通过使用开发人员工具检查XKCD主页,您知道<img>漫画图像的<div>元素位于id属性设置为的元素内部comic,因此选择器'#comic img'将从对象中获取正确的<img>元素BeautifulSoup

一些XKCD页面具有特殊的内容,而不是简单的图像文件。没关系; 您将跳过这些。如果您的选择器找不到任何元素,soup.select('#comic img')则将返回一个空白列表。发生这种情况时,程序可以仅打印错误消息并继续运行而无需下载图像。

否则,选择器将返回一个包含一个<img>元素的列表。您可以src从此<img>元素获取属性,并将其传递requests.get()给下载漫画的图像文件。

comicUrl值类似'http://imgs.xkcd.com/comics/heartbleed_explanation.png'—您可能已经注意到,它看起来很像文件路径。而事实上,你可以对comicUrl调用os.path.basename(),它将只返回URL的最后一部分:'heartbleed_explanation.png'。将图像保存到硬盘时,可以将其用作文件名。您使用将该名称与xkcd文件夹名称连接在一起,os.path.join()以便您的程序在Windows上使用反斜杠(\),在OS X和Linux上使用正斜杠(/)。现在您终于有了文件名,您可以调用open()'wb'“写入二进制”模式打开一个新文件。

请记住,在本章前面的内容中,要保存使用请求下载的文件,您需要遍历该iter_content()方法的返回值。for循环中的代码将图像数据的块(每个最大为100000个字节)写出到文件中,然后关闭文件。图像现在已保存到硬盘驱动器。

之后,选择器'a[rel="prev"]'标识属性设置为的<a>元素,您可以使用该元素的属性来获取先前漫画的URL,该URL存储在中。然后,循环将再次开始针对该漫画的整个下载过程。relprev<a>hrefurlwhile

漫画的上一页按钮在开发者元素中如图5-3所示

图5-3

代码如下:

import sys, os, requests, bs4
import logging
import tracebackLOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
LOG_PATH = None #os.path.join(os.getcwd(),'debugging.log')
logging.basicConfig(level=logging.DEBUG,format=LOG_FORMAT,datefmt = DATE_FORMAT ,filename=LOG_PATH #有了filename参数就不会直接输出显示到控制台,而是直接写入文件)
#downloadxkcd.py - Download every sigle XKCD comic."""
soup.select('div')  所有名为<div>的元素
soup.select('#author')  带有id属性为author的元素
soup.select('.notice')  所有使用CSS class属性名为notice的元素
soup.select('div span') 所有在<div>元素之内的<span>元素
soup.select('div > span')    所有直接在<div>元素之内的<span>元素,中间没有其他元素
soup.select('input[name]')  所有名为<input>,并有一个name属性,其值无所谓的元素
soup.select('input[type="button"]') 所有名为<input>,并有一个type属性,其值为 button的元素"""def downloadxkcd():fakeua = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36"}url = 'https://xkcd.com'    #starting urlorig_url = urlos.makedirs('xkcd', exist_ok = True)    #store comics in ./xkcdtry:while not  url.endswith('#'):# TODO: Download the page.logging.debug('downloading page %s ...' %(url))res = requests.get(url, headers = fakeua)res.raise_for_status()soup = bs4.BeautifulSoup(res.text, features="html.parser")#如果不指定features,在不同系统可能使用不同的解析器,此行造成以下告警内容,,提示十分友好# TODO: FInd the url of the comic image.comicElem = soup.select('#comic img') #所有在 comic 内的imgif [] == comicElem:print('could not find comic image.')else:# TODO: Download the image.  comicUrl = 'https:'+ comicElem[0].get('src')print('Downloadin image %s .................' %(comicUrl) ) res = requests.get(comicUrl, headers = fakeua)res.raise_for_status()# TODO: Savethe image to ./xkcdwith open(os.path.join('xkcd', os.path.basename(comicUrl)), 'wb') as imageFile:for chunk in res.iter_content(100000):imageFile.write(chunk)# TODO: Get the Prev button's urlprevLink = soup.select('a[rel="prev"]')[0]prev_image_url = prevLink.get('href')if None == prev_image_url:print('get prev image url result is None.')breakelse:url = orig_url + prev_image_urlprint('Done!')except:logging.debug('some error happend %s' %(traceback.format_exc()) )if __name__ == '__main__':downloadxkcd()

效果如下:

下载结果:

6. 自动化网页任务——selenium模块

requests和BeautifulSoup模块很了不起,只要你能弄清楚需要传递给requests.get()的URL。但是有时候这并不容易找到,或者你即将浏览的网站需要你先登录,selenium模块将让你的程序具有执行这种复杂任务的能力。通过该selenium模块,Python可以通过编程方式单击链接并填写登录信息来直接控制浏览器,几乎就像是人类在与页面进行交互一样。Selenium允许您以比Requests and Beautiful Soup更高级的方式与网页交互;但是由于它启动了Web浏览器,因此,例如,您只需要从Web下载一些文件,它就会变慢并且很难在后台运行。

以下例子使用Google浏览器完成,也可以使用火狐浏览器。

6.1 驱动Chrome浏览器

使用Python驱动Google浏览器完成网页访问,您需要安装Google浏览器,并将Google浏览器额驱动程序ChromeDriver同样安装到Google浏览器安装目录下:Google浏览器驱动程序ChromeDriver可以从ChromeDriver仓库下载对应版本,下载完以后,解压到Google浏览器应用目录下即可,我的目录如下:

Google浏览器版本为:76.0.3809.100,ChromeDriver下载对应版本:均可

现在,准备工作完成后,就可以使用Python驱动浏览器了。不过,即便这样,您还需要设置一些环境,比如讲Google浏览器的安装目录,即ChromeDriver驱动目录添加到您Windows的环境变量Path中,不过也可以不添加,那么久需要在您的Python程序中Web驱动初始化的时候指定chromedriver.exe的路径,例如:

from selenium import webdriver
#浏览器初始化设置为谷歌
driver = webdriver.Chrome('C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe')

这样,您就初始化了一个Chrome浏览器的驱动对象。

下面来使用这个对象做一些简单的事情,比如打开浏览器访问一个网站:

#! /usr/bin/python3
import logging,os
import traceback
from selenium import webdriver
#浏览器初始化设置为谷歌
driver = webdriver.Chrome('C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe')
driver.maximize_window() #浏览器窗口最大化LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
LOG_PATH = None #os.path.join(os.getcwd(),'debugging.log')
logging.basicConfig(level=logging.DEBUG,format=LOG_FORMAT,datefmt = DATE_FORMAT ,filename=LOG_PATH, #有了filename参数就不会直接输出显示到控制台,而是直接写入文件filemode='w')def browser_python(url):print('try to conect to %s...' %(url))try:driver.get(url)except:print('some bad happend :%s' %(traceback.format_exc()) )if __name__ == '__main__':#url = 'http://inventwithpython.com/'#browser_python(url) #访问python 自动化编程的电子书网站#访问我的QQ空间,这里需要登录,关于登录程序暂时不添加url = 'https://user.qzone.qq.com/2281284114/infocenter'browser_python(url)

你会注意到,当webdriver.Chrome()调用时,浏览器启动了,如果不添加任何动作,那么将初始化一个Chrome浏览器空白页。

对值driver调用type(),得到数据类型为:<class 'selenium.webdriver.chrome.webdriver.WebDriver'>,数据类型为WebDriver。

6.2 在Web页面中寻找元素

WebDriver对象有多种方法,用于在页面中寻找元素。他们被分成find_element_*和find_elements_*方法。find_element_*返回一个WebElement对象,代表页面中匹配查询的第一个元素,find_elements_*返回WebElement_*对象的列表。包含页面中所有匹配的元素。

表6-1 列出了find_element_*和find_elements_*方法的几个例子,他们在变量driver中保存的WebDriver对象上调用。

表6-1 selenium的WebDriver方法,用于寻找元素
WebElement 对象/列表返回
browser.find_element_by_class_name(name)  
browser.find_elements_by_class_name(name) 使用CSS类name的元素
browser.find_element_by_css_selector(selector)  
browser.find_elements_by_css_selector(selector) 符合CSS selector的元素
browser.find_element_by_id(id)  
browser.find_elements_by_id(id) 匹配id属性值的元素
browser.find_element_by_link_text(text)  
browser.find_elements_by_link_text(text) 与text提供的内容完全匹配的<a>元素
browser.find_element_by_partial_link_text(text)  
browser.find_elements_by_partial_link_text(text) 包含text提供的内容的<a>元素
browser.find_element_by_name(name)  
browser.find_elements_by_name(name) 匹配name属性值的元素
browser.find_element_by_tag_name(name)  
browser.find_elements_by_tag_name(name) 匹配标签name的<a>元素(不区分大小写;<a>元素匹配'a'和'A')

*_by_tag_name()方法外,所有方法的参数均区分大小写。如果页面上不存在与该方法要查找的内容匹配的元素,则该selenium模块将引发NoSuchElement异常。如果您不希望该异常导致程序崩溃,请在代码中添加tryexcept语句。

一旦有了WebElement对象,就可以读取表6-2宗的属性或调用其中的方法,了解WebElement对象的更多内容

表 6-2 WebElement的属性和方法
属性或方法 描述
tag_name 标签名称,例如'a'表示<a>元素
get_attribute(name) 该元素name属性的值
text 元素内的文本,例如<span>hello</span>中的hello
clear() 对于文本字段或文本区域元素,清除键入其中的文本
is_displayed() 元素可见,返回True;否则返回False
is_enabled() 对于输入元素,如果已启用该元素,则返回True;否则返回False 
is_selected() 对于复选框或单选框元素,如果该元素被选中,返回True;否则返回false
location 一个字典,包含键'x'和'y',表示元素在页面中的位置 的字典

6.3 点击页面

find_element_*和find_elements_*方法返回的WebElement对象有一个click()方法,模拟鼠标在该元素上点击。这个方法可以用于链接跳转,选择单选按钮,点击提交按钮,或者处罚钙元素被鼠标点击时发生的任何事情。例子:比如我们访问 http://inventwithpython.com/ ,首页有对应每一本书籍的在线阅读选项框,点击就会跳转到该书籍的电子在线阅读界面:我们将鼠标放在'Read Online for Free'选项框上,右键选择检查,就会跳出该选项的开发者界面,这样我们就知道我们将要获取的链接关键字和链接了

def click_link(url):try:#先访问网页,返回WebElementdriver.get(url)#其实是寻找href'''<a href="https://automatetheboringstuff.com/" class="btn btn-primary">Read Online for Free</a>'''link_login = driver.find_element_by_link_text('Read Online for Free') #找到并返回第一个link_login.click() #模拟点击except:print('some bad happend :%s' %(traceback.format_exc()) )

网页成功跳转到在线阅读页面:https://automatetheboringstuff.com/

6.4 填写并提交表单(模拟自动登录QQ空间)

向Web页面的文本字段发送击键,只要找到那个文本字段的<input> 或者 <textarea>元素,然后调用send_keys()方法。我们可以利用python模拟邓丽QQ空间或QQ邮箱。这里要注意几点:

1> iframe 框架切换 具体使用可以参考 selenium中iframe的切换

百度:IFRAME是HTML标签,作用是文档中的文档,或者浮动的框架(FRAME)。iframe元素会创建包含另外一个文档的内联框架(即行内框架)

核心属性

属性

描述

class

classname

规定元素的类名(classname)

id

id

规定元素的特定id

style

style_definition

规定元素的行内样式(inline style)

title

text

规定元素的额外信息(可在工具提示中显示)

如QQ空间登录界面的frame 框架

2> 定位input元素,键入内容

测试程序:目前QQ登录需要验证滑动条,因此不能一步登录,但是基本功能已经完成,即:打开浏览器,切换到账号密码登录界面,输入账号密码,只需要手动滑动验证码就可以登录

代码如下:

def login_count():try:driver.get('https://qzone.qq.com/')'''定位frame <iframe id="login_frame" name="login_frame" height="100%" scrolling="no" width="100%" frameborder="0" src="https://xui.ptlogin2.qq.com/cgi-bin/xlogin?proxy_url=https%3A//qzs.qq.com/qzone/v6/portal/proxy.html&amp;daid=5&amp;&amp;hide_title_bar=1&amp;low_login=0&amp;qlogin_auto_login=1&amp;no_verifyimg=1&amp;link_target=blank&amp;appid=549000912&amp;style=22&amp;target=self&amp;s_url=https%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&amp;pt_qr_app=手机QQ空间&amp;pt_qr_link=https%3A//z.qzone.com/download.html&amp;self_regurl=https%3A//qzs.qq.com/qzone/v6/reg/index.html&amp;pt_qr_help_link=https%3A//z.qzone.com/download.html&amp;pt_no_auth=0"></iframe>'''#登录表单在页面的框架中,所以要切换到该框架driver.switch_to.frame('login_frame') #没有此条语句将不能跳转登录输入login_switch = driver.find_element_by_id('switcher_plogin')login_switch.click() #点击通过账号密码交互登录InName = driver.find_element_by_id('u')InName.clear() #清除框内的内容InName.send_keys('123456789')inPass = driver.find_element_by_id('p') #输入密码inPass.clear()inPass.send_keys('******')#inPass.submit() 提交表单,实际应用中不常用到,登录界面都是多种多样的login_click = driver.find_element_by_id('login_button')login_click.click() #登录except:print('some bad happend :%s' %(traceback.format_exc()) )

6.5 发送特殊键

Selenium有一个用于键盘键的模块,这些模块无法键入字符串值,其功能很像转义符。这些值存储在selenium.webdriver.common.keys模块的属性中。由于这是一个很长的模块名称,因此from selenium.webdriver.common.keys import Keys在程序顶部运行会容易得多。如果这样做的话,那么您只需输入Keys.*** 即可。

表 6-3 列出了常用的Keys变量

表6-3 selenium.webdriver.common.keys模块中常用的变量
属性 含义
Keys.DOWN,Keys.UP,Keys.LEFT,Keys.RIGHT 键盘方向键
Keys.ENTER, Keys.RETURN 回车键和换行键
Keys.HOME,Keys.END,Keys.PAGE_DOWN,Keys.PAGE_UP home键,end键,PageUp键,PageDown键
Keys.ESCAPE,Keys.BACK_SPACE,Keys.DELETE Esc键,Backspace键和删除键delete
Keys.F1,Keys.F2,...,Keys.F12 键盘顶部F1到F12键
Keys.TAB Tab键

6.6 点击浏览器按钮

方法 功能
browser.back() 单击上一步或返回按钮
browser.forward() 点击前进按钮
browser.refresh() 点击刷新/重新加载按钮
browser.quit() 点击关闭窗口按钮

关于selenium 的更多信息

selenuum作用远远超出了此处描述的功能。它可以修改浏览器的Cookie,截取网页的屏幕截图以及运行自定义JavaScript。要了解有关这些功能的更多信息,可以访问http://selenium-python.readthedocs.org/上的Selenium文档。

小结

大多数无聊的任务不仅限于计算机上的文件。能够以编程方式下载网页会将您的程序扩展到Internet。该requests模块使下载变得简单明了,并且具有HTML概念和选择器的一些基本知识,您可以利用该BeautifulSoup模块来解析下载的页面。

但是要完全自动化任何基于Web的任务,您需要通过该selenium模块直接控制Web浏览器。该selenium模块将允许您登录网站并自动填写表格。由于Web浏览器是通过Internet发送和接收信息的最常用方法,因此这是您的程序员工具包中的一项强大功能。

Python编程-让繁琐的工作自动化(十一)从Web爬取信息相关推荐

  1. python处理csv文件案例_让繁琐的工作自动化——python处理CSV文件

    让繁琐的工作自动化--python处理CSV文件 CSV:CSV文件是一种简化的电子表格,不同于Excle(二进制文件),CSV是纯文本文件. 1.环境 python3.8 pycharm2020.1 ...

  2. json 插入数据_让繁琐的工作自动化——python处理JSON文件

    让繁琐的工作自动化--python处理JSON文件 不得不说,python真TMD香.由于python解析json过于简洁,我只好写个实例了,不然文章太短. 1.环境 python3.8 pychar ...

  3. 会python编程可以做什么工作_学Python编程到底能做什么工作?

    学Python编程能做什么工作?随着人工智能发展,学习python语言的人员有更多的岗位机会,python从事的职业广泛,从游戏到AI人工智能能都可以用Python实现.除了编程,各种岗位的人都应该学 ...

  4. python编程可以实现办公自动化_学Python编程能做什么工作?从事什么岗位?

    学Python编程能做什么工作?随着人工智能发展,学习python语言的人员有更多的岗位机会,python从事的职业广泛,从游戏到AI人工智能能都可以用Python实现.除了编程,各种岗位的人都应该学 ...

  5. Python爬虫《自动化学报》数据爬取与数据分析

    Python爬虫<自动化学报>数据爬取与数据分析 文章目录 Python爬虫<自动化学报>数据爬取与数据分析 前言 一.代码 二.结果展示 三.爬虫实现 1.准备 2.获取网页 ...

  6. Python从放弃到入门,公众号历史文章爬取成pdf的项目实践与自主学习法

    这篇文章不谈江流所专研的营销与运营,而聊一聊技能学习之路,聊一聊Python这门最简单的编程语言该如何学习,我完成的第一个Python项目,将任意公众号的所有历史文章导出成PDF电子书. 或许我这个P ...

  7. [python爬虫] 招聘信息定时系统 (一).BeautifulSoup爬取信息并存储MySQL

    这系列文章主要讲述,如何通过Python爬取招聘信息,且爬取的日期为当前天的,同时将爬取的内容保存到数据库中,然后制作定时系统每天执行爬取,最后是Python调用相关库发送短信到手机.         ...

  8. Python网络爬虫与信息提取(17)—— 题库爬取与整理+下载答案

    前言 上一节实现了题目的整理,没整理答案是不完整的,所以这一节加上答案的爬取. 上一节地址:Python网络爬虫与信息提取(16)-- 题库爬取与整理 效果 思路 爬答案有点难搞,像这种题库的答案都是 ...

  9. Python爬虫新手入门教学(十八):爬取yy全站小视频

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. Python爬虫.数据分析.网站开发等案例教程视频免费在线观看 https://space. ...

最新文章

  1. 聚焦3D地形编程第五章GeomipMapping for the CLOD
  2. c语言鼠标下棋,c语言写的鼠标操作的五子棋游戏,欢迎观赏!
  3. Php的基本语法学习
  4. 【ABAP】销售订单Billing Plan创建
  5. PHP监測memcache服务端的执行状况
  6. django文件上传,只上传一张
  7. solaris 关闭、释放socket端口
  8. element-ui 源码学习
  9. 一般凸二次规划的有效集算法 Matlab程序
  10. Smart3D基础理论
  11. 什么样的联系人管理软件最有效?这里有管理联系人的3款实用软件
  12. 对话阿里云弹性计算负责人褚霸:把计算做到极致,关键还不加价!
  13. Keras:使用预训练模型迁移学习单通道灰度图像
  14. eui怎么在名字后面显示服务器,EUI如何打开经验条 显示经验条其实很简单
  15. 扇贝python课程打卡_Learning by doing——百日“扇贝打卡” 历程展望
  16. 旋转编码器怎么调零点?
  17. HDU-5868-Different Circle Permutation(快速幂求fib,单数欧拉函数(1e9规模),oeis)...
  18. HTML5 拖放Drag和drop用法以及事件介绍
  19. java道路上需要坚挺
  20. 這些食物絕對不能放隔夜(圖)

热门文章

  1. GPS定位系统普遍存在的问题
  2. android手机采集,Android手机直播之采集技术分析
  3. IBM ACE User Defined Node
  4. [Russell Han] 24 | 数据库基础 | 关系模型
  5. Vue3+TS使用element-plus 动态Icon图标
  6. 猫哥教你写爬虫 037--爬虫-宝宝要听歌
  7. Android 中英文切换的实现。
  8. 如何把一张透明的图片做成不透明的
  9. [PC] 微软账号连接不上
  10. QStringList去除重复项