web 3d机械手臂
使用基本的Python模块可以编写与网站交互的脚本,但是如果不需要的话,则不需要这样做。 Python 2.x中的urllib
和urllib2
模块,以及Python 3.0中的统一urllib.*
子软件包,在URL末尾获取资源方面做得很好。 但是,当您想要与在Web页面上找到的内容进行某种程度的复杂交互时,您确实需要机械化库( 有关下载链接,请参见参考资料 )。
自动化Web抓取或其他模拟用户与Web站点交互的主要困难之一是服务器使用cookie来跟踪会话进度。 显然,cookie是HTTP标头的一部分,并且在urllib
打开资源时固有地可见。 此外,标准模块Cookie
( http.cookie
在Python 3)和cookielib
( http.cookiejar
在Python 3)以比原始文本的处理的更高级别处理这些头的帮助。 即使这样,在此级别上执行此处理也比必要的麻烦。 机械化库将这种处理方式带入了更高的抽象水平,并使您的脚本(或交互式Python Shell)的行为与实际的Web浏览器非常相似。
Python的机械化灵感来自Perl的WWW:Mechanize
,它具有类似的功能范围。 当然,作为一个长期使用Python的人,我发现机械化功能更加强大,它似乎遵循了这两种语言的通用模式。
机械化的密友是同样出色的库Beautiful Soup (请参阅参考资料中的下载链接)。 对于在实际Web页面中经常发现的近似有效HTML,这是一个奇妙的“草率解析器”。 您无需将Beautiful Soup与mechanize一起使用,反之亦然,但是在与“实际存在的Web”进行交互时,您经常会希望同时使用这两个工具。
一个真实的例子
我在多个编程项目中使用过机械化。 最近的一个项目是从流行的网站收集与某些条件匹配的名称列表。 该站点具有一些搜索功能,但没有用于执行此类搜索的任何官方API。 尽管读者也许可以更具体地猜测我在做什么,但我将更改所提供代码的细节,以避免在被抓取的网站或客户上提供过多的信息。 在一般形式下,非常类似于我介绍的代码将在类似的任务中通用。
开始的工具
在实际开发Web抓取/分析代码的过程中,我发现能够以交互方式查看,戳戳和制作Web页面的内容,以弄清相关Web页面上实际发生的事情,这是非常宝贵的。 通常,这些是站点内的页面集,这些页面集可以通过查询动态生成(但具有一致的模式),也可以按照相当严格的模板预先生成。
这样的互动实验的一个有价值的方法是使用机械化本身Python的外壳内,特别是在像IPython的增强Shell(请参阅相关信息中的链接)。 通过这种方式进行探索,您可以在编写最终脚本以执行您要在生产中进行的交互之前,请求各种链接的资源,提交表单,维护或操作站点cookie等。
但是,我发现在实际的现代Web浏览器中,与网站的许多实验性交互都可以更好地完成。 看到一个方便呈现的页面,可以更快地了解给定页面或表单所发生的情况。 问题在于,仅渲染页面只能提供一半的故事,也许还不到一半。 使用“页面源”可以使您更进一步。 为了真正了解给定网页或与Web服务器的交互序列背后的内容,我发现还需要更多。
为了获得这些勇气,我通常使用Firebug(请参阅参考资料中的链接)或Firefox的Web开发器插件(或最新Safari版本中的内置可选Develop菜单,但这是针对不同的读者)。 所有这些工具使您可以执行诸如显示表单字段,显示密码,检查页面DOM,查看或运行Javascript,监视Ajax流量等操作。 比较这些工具的好处和怪癖是另一篇文章,但是如果您进行任何面向Web的编程,请务必熟悉它们。
无论您使用什么特定工具来尝试与之进行自动交互的网站进行试验,与编写执行任务所需的紧凑的机械化代码相比,您可能要花费更多的时间弄清楚该站点的实际运行情况。
搜索结果刮板
出于上面提到的项目的目的,我将一百行脚本分为两个函数:
- 检索我感兴趣的所有结果
- 从检索到的页面中提取我感兴趣的信息
为了方便开发,我以此方式组织了脚本。 当我开始任务时,我知道我需要弄清楚如何做这两件事。 我感觉到我想要的信息是在一般页面上的,但是我还没有检查这些页面的具体布局。
通过首先检索一批页面并将它们仅保存到磁盘,我可以回到从那些保存的文件中提取我关心的信息的任务。 当然,如果您的任务涉及使用检索到的信息来在同一会话中制定新的交互,则需要使用略有不同的开发步骤序列。
因此,首先让我们看一下我的fetch()
函数:
清单1.获取页面内容
import sys, time, os
from mechanize import Browser
LOGIN_URL = 'http://www.example.com/login'
USERNAME = 'DavidMertz'
PASSWORD = 'TheSpanishInquisition'
SEARCH_URL = 'http://www.example.com/search?'
FIXED_QUERY = 'food=spam&' 'utensil=spork&' 'date=the_future&'
VARIABLE_QUERY = ['actor=%s' % actor for actor in
('Graham Chapman',
'John Cleese',
'Terry Gilliam',
'Eric Idle',
'Terry Jones',
'Michael Palin')]
def fetch():
result_no = 0 # Number the output files
br = Browser() # Create a browser
br.open(LOGIN_URL) # Open the login page
br.select_form(name="login") # Find the login form
br['username'] = USERNAME # Set the form values
br['password'] = PASSWORD
resp = br.submit() # Submit the form
# Automatic redirect sometimes fails, follow manually when needed
if 'Redirecting' in br.title():
resp = br.follow_link(text_regex='click here')
# Loop through the searches, keeping fixed query parameters
for actor in in VARIABLE_QUERY:
# I like to watch what's happening in the console
print >> sys.stderr, '***', actor
# Lets do the actual query now
br.open(SEARCH_URL + FIXED_QUERY + actor)
# The query actually gives us links to the content pages we like,
# but there are some other links on the page that we ignore
nice_links = [l for l in br.links()
if 'good_path' in l.url
and 'credential' in l.url]
if not nice_links: # Maybe the relevant results are empty
break
for link in nice_links:
try:
response = br.follow_link(link)
# More console reporting on title of followed link page
print >> sys.stderr, br.title()
# Increment output filenames, open and write the file
result_no += 1
out = open(result_%04d' % result_no, 'w')
print >> out, response.read()
out.close()
# Nothing ever goes perfectly, ignore if we do not get page
except mechanize._response.httperror_seek_wrapper:
print >> sys.stderr, "Response error (probably 404)"
# Let's not hammer the site too much between fetches
time.sleep(1)
在对感兴趣的站点进行了交互式探索之后,我发现希望执行的查询具有一些固定元素和一些可变元素。 我只是将它们连接成一个大的GET
请求,然后查看“结果”页面。 反过来,该结果列表包含指向我实际需要的资源的链接。 因此,我遵循这些规则(添加了一些try
/ except
块,以防某些方式无法正常工作)并保存在这些内容页面上找到的所有内容。
很简单吧? 机械化可以做的还不止这些,但是这个简短的示例向您展示了它的功能。
处理结果
至此,我们完成了机械化。 剩下的就是弄清楚我们在fetch()
循环中保存的fetch()
HTML文件。 进程的批处理性质使我可以清晰地将它们分开,但是显然在另一个程序中, fetch()
和process()
可能会更紧密地交互。 Beautiful Soup使得后处理比最初的获取更加容易。
对于此批处理任务,我们希望从在所获取的各种Web页面上找到的点点滴滴中生成表格逗号分隔值(CSV)数据。
清单2.以奇妙的方式使数据有条不紊,以Beautiful Soup结尾
from glob import glob
from BeautifulSoup import BeautifulSoup
def process():
print "!MOVIE,DIRECTOR,KEY_GRIP,THE_MOOSE"
for fname in glob('result_*'):
# Put that sloppy HTML into the soup
soup = BeautifulSoup(open(fname))
# Try to find the fields we want, but default to unknown values
try:
movie = soup.findAll('span', {'class':'movie_title'})[1].contents[0]
except IndexError:
fname = "UNKNOWN"
try:
director = soup.findAll('div', {'class':'director'})[1].contents[0]
except IndexError:
lname = "UNKNOWN"
try:
# Maybe multiple grips listed, key one should be in there
grips = soup.findAll('p', {'id':'grip'})[0]
grips = " ".join(grips.split()) # Normalize extra spaces
except IndexError:
title = "UNKNOWN"
try:
# Hide some stuff in the HTML <meta> tags
moose = soup.findAll('meta', {'name':'shibboleth'})[0]['content']
except IndexError:
moose = "UNKNOWN"
print '"%s","%s","%s","%s"' % (movie, director, grips, moose)
process()
的代码是对“美丽汤”的印象深刻的第一印象。 读者应阅读其文档,以获取有关模块详细信息的更多信息,但在此代码段中可以很好地体现总体感觉。 大多数汤代码由对页面的一些.findAll()
调用组成,该页面可能仅是格式正确HTML。 这里抛出一些类似于DOM的.parent
, nextSibling
和previousSibling
属性。 这些类似于Web浏览器的“怪癖”模式。 我们发现汤是不是很解析树; 它更像一袋装满蔬菜的麻袋,可能会混入汤中(过滤一个比喻)。
结论
像我这样的老混蛋,甚至是一些年轻的读者,都会记得使用TCL Expect(或用Python和许多其他语言编写的作品)编写脚本的极大乐趣。 与外壳程序(包括远程程序,例如telnet,ftp,ssh等)的自动化交互相对简单,因为所有内容都在会话中显示 。 Web交互稍微有些微妙,因为信息在标头和正文之间划分,并且各种相关资源通常与href
链接,框架,Ajax等捆绑在一起。 但是,原则上,您可以只使用wget
类的工具来检索Web服务器可能提供的每个字节,然后运行与其他连接协议完全相同的Expect脚本样式。
在实践中,很少有程序员像我建议的wget
+ Expect方法那样致力于老式方法。 机械化仍然具有与那些不错的Expect脚本相同的熟悉和舒适感,并且即使不容易编写也一样容易编写。 Browser()
对象命令(例如.select_form()
.submit()
和.follow_link()
实际上只是最简单,最明显的说法,即“寻找并发送”,同时捆绑了所有复杂的功能。 Web自动化框架中需要的状态和会话处理。
翻译自: https://www.ibm.com/developerworks/web/library/l-python-mechanize-beautiful-soup/index.html
web 3d机械手臂