前面已經說了,其實學習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