前面已经说了,其实学习Python的过程,不少时候就是在学习如何使用第三方模块,完成本身须要的功能。 html
关于Python的第三方库类库,其实网上不少不少相关资料。 html5
其中,官网的Python库:Python Package Index,其中有N多N多的库,有须要的人,能够去那里找找。 python
其余的网上的N多资源中,我以为值得看看的有: web
下面就来总结一下,我的对于一些第三方模块的使用心得。 正则表达式
Python的BeautifulSoup模块,能够帮助你实现HTML和XML的解析 网页爬虫
先说一下,通常写网页爬虫,即抓取网页的html源码等内容,而后分析,提取相应的内容。 浏览器
这种分析html内容的工做,若是只是用普通的正则表达式re模块去一点点匹配的话,对于内容简单点的网页分析,仍是基本够用。 app
可是对于工做量很大,要分析的内容很繁杂的html,那么用re模块,就会发现没法实现,或很难实现。 wordpress
而使用beautifulsoup模块去帮你实现分析html源码的工做的话,你就会发现,事情变得如此简单,极大地提升了分析html源码的效率。 函数
好比我这里的想要实现博客搬家以前,想要抓取对应的博客中的内容,就须要先去打开一个URL地址,去解析其中的内容,找到第一个固定连接,而后一点点分析HTML中的内容,抓去下来,导出wordpress所须要的xml文件等。
这其中对于HTML的分析,就能够利用BeautifulSoup这个模块了。
更多内容参见"Beautiful Soup 中文文档"
其中,原先连接:
http://www.crummy.com/software/BeautifulSoup/documentation.zh.html
已失效,最新的可用的地址是:
http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html
想要下载的话,这是BeautifulSoup的官网,其中能够下载到最新的版本:
http://www.crummy.com/software/BeautifulSoup/
下面就介绍一些Beautifulsoup使用过程当中的心得和注意事项:
在使用Beautifulsoup过程当中,对于大多数html源码,经过指定正确的编码,或者自己是默认UTF-8编码而无需指定编码类型,其均可以正确解析html源码,获得对应的soup变量。
而后就接着去利用soup实现你所想要的功能了。
可是有时候会发现,有些html解析后,有些标签等内容丢失了,即所获得的soup不是所指望的完整的html的内容。
这时候,极可能遇到了非法的html,即其中可能包含了一些不合法的html标签等内容,致使Beautifulsoup虽然能够解析,没有报错,可是实际上获得的soup变量,内容缺失了一部分了。
好比我就遇到过很多这样的例子:
以前在为BlogsToWordPress添加Blogbus支持过程当中去解析Blogbus的帖子的时候,遇到一个特殊的帖子:http://ronghuihou.blogbus.com/logs/89099700.html,其中一堆的非html5的代码中,包含了这样一段html5的代码的写法,即标签属性值不加括号的:
<SCRIPT language=JavaScript> document.oncontextmenu=new Function("event.returnValue=false;"); //禁止右键功能,单击右键将无任何反应 document.onselectstart=new Function( "event.returnValue=false;"); //禁止先择,也就是没法复制 </SCRIPT language=JavaScript>
结果致使Beautifulsoup解析错误,获得的soup中,找不到所须要的各类class等属性值。
对应的解决办法就是,把这部分的代码删除掉,而后再解析就能够了:
其中一堆的非html5的代码中,包含了这样一段html5的代码的写法,即标签属性值不加括号的:
foundInvliadScript = re.search("<SCRIPT language=JavaScript>.+</SCRIPT language=JavaScript>", html, re.I | re.S ); logging.debug("foundInvliadScript=%s", foundInvliadScript); if(foundInvliadScript): invalidScriptStr = foundInvliadScript.group(0); logging.debug("invalidScriptStr=%s", invalidScriptStr); html = html.replace(invalidScriptStr, ""); logging.debug("filter out invalid script OK"); soup = htmlToSoup(html);
以前在给BlogsToWordpress添加新浪博客的支持的过程当中
遇到不少新浪博客的帖子的html中,包含不少判断浏览器版本的相关代码:
<!–[if lte IE 6]> xxx xxx <![endif]–>
由此致使Beautifulsoup解析html不正常。
接上面那个解析新浪博客帖子的例子,期间又遇到另一个问题,对于一些特殊帖子:http://blog.sina.com.cn/s/blog_5058502a01017j3j.html
其包含特殊的好几十个font标签且是一个个嵌套的代码,致使没法Beautifulsoup没法解析html,后来把对应嵌套的font标签删除掉,才能够正常解析。
相关python代码为:
# handle special case for http://blog.sina.com.cn/s/blog_5058502a01017j3j.html processedHtml = processedHtml.replace('<font COLOR="#6D4F19"><font COLOR="#7AAF5A"><font COLOR="#7AAF5A"><font COLOR="#6D4F19"><font COLOR="#7AAF5A"><font COLOR="#7AAF5A">', ""); processedHtml = processedHtml.replace("</FONT></FONT></FONT></FONT></FONT></FONT>", "");
遇到其余相似的问题,也能够去删除或替换出错代码,便可解决问题。
不过须要说明的是,不少时候,你未必很容易就找到出错的代码。
想要找到出错的代码,更多的时候,须要你一点点调试,一点点的删除看似可疑的一些html源码,而后最终才能定位到出错的代码,而后删除掉后,才能够正常工做的。
BeautifulSoup处理这种html源码:
<a href="http://creativecommons.org/licenses/by/3.0/deed.zh" target="_blank">版权声明</a>
后,是能够经过
soup.a['href']
去得到对应的href属性值的。
可是,想要去得到当前的某个未知的BeautifulSoup.Tag中,一共存在多少个属性,以及每一个属性的值的时候,殊不知道如何下手了。
好比对于以下html源码:
<p class="cc-lisence" style="line-height:180%;">......</p>
想要得知,一共有两个属性,分别是class和style,而后就能够经过上面的方法,去得到对应的属性值了。
对此问题,虽然看到了官网的解释:Tags的属性中的“你能够将Tag当作字典来访问标签的属性”
可是仍是没法经过:
if("class" in soup)
的方式去判断soup中是否存在class属性,由于此时soup是Beautifulsoup.Tag类型变量,而不是dict变量。
而且,若是去强制将soup转换成为dict变量:
soupDict = dict(soup)
会报错的。
最后,仍是无心间发现,原来Beautifulsoup.Tag是有个attrs属性的,其能够得到对应的元组列表,每个元组是对应属性名和属性值:
attrsList = soup.attrs; print "attrsList=",attrsList;
attrsList= [(u'class', u'cc-lisence'), (u'style', u'line-height:180%;')]
这样,就能够从元组列表中,本身去转换,得到属性的列表或字典变量了,就能够接着按照本身意愿去处理了。
![]() |
提示 |
---|---|
另外,此处也经过 soup.name 得到了该tag的名字 而想要得到整个soup变量全部的属性和方法的话,能够用经典的dir去打印出来: print "dir(soup)=",dir(soup); 此处的打印输出为: dir(soup)= ['BARE_AMPERSAND_OR_BRACKET', 'XML_ENTITIES_TO_SPECIAL_CHARS', 'XML_SPECIAL_CHARS_TO_ENTITIES', '__call__', '__contains__', '__delitem__', '__doc__', '__eq__', '__getattr__', '__getitem__', '__init__', '__iter__', '__len__', '__module__', '__ne__', '__nonzero__', '__repr__', '__setitem__', '__str__', '__unicode__', '_convertEntities', '_findAll', '_findOne', '_getAttrMap', '_invert', '_lastRecursiveChild', '_sub_entity', 'append', 'attrMap', 'attrs', 'childGenerator', 'containsSubstitutions', 'contents', 'convertHTMLEntities', 'convertXMLEntities', 'decompose', 'escapeUnrecognizedEntities', 'extract', 'fetch', 'fetchNextSiblings', 'fetchParents', 'fetchPrevious', 'fetchPreviousSiblings', 'fetchText', 'find', 'findAll', 'findAllNext', 'findAllPrevious', 'findChild', 'findChildren', 'findNext', 'findNextSibling', 'findNextSiblings', 'findParent', 'findParents', 'findPrevious', 'findPreviousSibling', 'findPreviousSiblings', 'first', 'firstText', 'get', 'has_key', 'hidden', 'insert', 'isSelfClosing', 'name', 'next', 'nextGenerator', 'nextSibling', 'nextSiblingGenerator', 'parent', 'parentGenerator', 'parserClass', 'prettify', 'previous', 'previousGenerator', 'previousSibling', 'previousSiblingGenerator', 'recursiveChildGenerator', 'renderContents', 'replaceWith', 'setup', 'substituteEncoding', 'toEncoding'] 有须要的话,能够对这些属性和方法,都尝试一下,以便更加清楚其含义。 刚写完上面这句话呢,而后本身随便测试了一下attrMap: attrMap = soup.attrMap; print "attrMap=",attrMap; 而后就很惊喜的发现,原来此处的attrMap,就是我程序中所须要的属性的dict变量啊: attrMap= {u'style': u'line-height:180%;', u'class': u'cc-lisence'} 这样,就又省去了我程序中将attrs转换为dict变量的操做了,更加提升了效率。在次感谢Beautifulsoup的开发者。 |
在使用Beautifulsoup的find/finaAll等函数时候,常见用法都是传递字符串自己,好比:
foundIncontentTitle = lastItem.find(attrs={"class":"a-incontent a-title"});
能够找到:
<a href="/serial_story/item/7d86d17b537d643c70442326" class="a-incontent a-title" target=_blank>I/O-Programming_HOWTO(上)zz</a>
中的值。
可是却没法匹配:
<a href="/serial_story/item/0c450a1440b768088fbde426" class="a-incontent a-title cs-contentblock-hoverlink" target="_blank">为何幸运的人总幸运倒霉的人老倒霉-1 斯宾塞·约翰逊著</a>
即,class的值是:a-incontent a-title cs-contentblock-hoverlink,而不只仅是a-incontent a-title
此时,若是想要匹配才class,使用传统的方法,则须要写两个find去匹配,然后来得知,原来find/findAll等函数的参数中,也可使用正则表达式的,因此就用了:
titleP = re.compile("a-incontent a-title(\s+?\w+)?");# also match a-incontent a-title cs-contentblock-hoverlink foundIncontentTitle = lastItem.find(attrs={"class":titleP});
就能够一次性匹配,a-incontent a-title,a-incontent a-title cs-contentblock-hoverlink,以及将来更多可能的a-incontent a-title xxx了。
感叹一句,Beautifulsoup,作的的确很好用,特此感谢做者。
![]() |
![]() |
![]() |
3.3. Python中自带模块的使用心得 | ![]() |
第 4 章 crifan的Python库:crifanLib.py |
特别感谢Crifan Li对python及几个第三方库的总结,这里提供做者的网站,但愿给更多的人提供帮助:http://www.crifan.com/files/doc/docbook/python_summary/release/htmls/index.html