青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 319, comments - 22, trackbacks - 0, articles - 11
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

Python XML

Posted on 2011-05-22 21:34 RTY 閱讀(3565) 評論(0)  編輯 收藏 引用 所屬分類: Python轉(zhuǎn)載隨筆

URL= http://woodpecker.org.cn/diveintopython3/xml.html

你的位置: HomeDive Into Python 3

難度等級(jí): ♦♦♦♦♢

XML

? In the archonship of Aristaechmus, Draco enacted his ordinances. ?
Aristotle

顯示目錄

概述#

這本書的大部分章節(jié)都是以樣例代碼為中心的。但是XML這章不是;它以數(shù)據(jù)為中心。最常見的XML應(yīng)用為“聚合訂閱(syndication feeds)”,它用來展示博客,論壇或者其他會(huì)經(jīng)常更新的網(wǎng)站的最新內(nèi)容。大多數(shù)的博客軟件都會(huì)在新文章,新的討論區(qū),或者新博文發(fā)布的時(shí)候自動(dòng)生成和更新feed。我們可以通過“訂閱(subscribe)”feed來關(guān)注它們,還可以使用專門的“feed聚合工具(feed aggregator)”,比如Google Reader

以下的XML數(shù)據(jù)是我們這一章中要用到的。它是一個(gè)feed — 更確切地說是一個(gè)Atom聚合feed

跳過該代碼清單

<?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
<title>dive into mark</title>
<subtitle>currently between addictions</subtitle>
<id>tag:diveintomark.org,2001-07-29:/</id>
<updated>2009-03-27T21:56:07Z</updated>
<link rel='alternate' type='text/html' />
<link rel='self' type='application/atom+xml' />
<entry>
<author>
<name>Mark</name>
<uri>http://diveintomark.org/</uri>
</author>
<title>Dive into history, 2009 edition</title>
<link rel='alternate' type='text/html'
/>
<id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
<updated>2009-03-27T21:56:07Z</updated>
<published>2009-03-27T17:20:42Z</published>
<category scheme='http://diveintomark.org' term='diveintopython'/>
<category scheme='http://diveintomark.org' term='docbook'/>
<category scheme='http://diveintomark.org' term='html'/>
<summary type='html'>Putting an entire chapter on one page sounds
bloated, but consider this &amp;mdash; my longest chapter so far
would be 75 printed pages, and it loads in under 5 seconds&amp;hellip;
On dialup.</summary>
</entry>
<entry>
<author>
<name>Mark</name>
<uri>http://diveintomark.org/</uri>
</author>
<title>Accessibility is a harsh mistress</title>
<link rel='alternate' type='text/html'
/>
<id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id>
<updated>2009-03-22T01:05:37Z</updated>
<published>2009-03-21T20:09:28Z</published>
<category scheme='http://diveintomark.org' term='accessibility'/>
<summary type='html'>The accessibility orthodoxy does not permit people to
question the value of features that are rarely useful and rarely used.</summary>
</entry>
<entry>
<author>
<name>Mark</name>
</author>
<title>A gentle introduction to video encoding, part 1: container formats</title>
<link rel='alternate' type='text/html'
/>
<id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id>
<updated>2009-01-11T19:39:22Z</updated>
<published>2008-12-18T15:54:22Z</published>
<category scheme='http://diveintomark.org' term='asf'/>
<category scheme='http://diveintomark.org' term='avi'/>
<category scheme='http://diveintomark.org' term='encoding'/>
<category scheme='http://diveintomark.org' term='flv'/>
<category scheme='http://diveintomark.org' term='GIVE'/>
<category scheme='http://diveintomark.org' term='mp4'/>
<category scheme='http://diveintomark.org' term='ogg'/>
<category scheme='http://diveintomark.org' term='video'/>
<summary type='html'>These notes will eventually become part of a
tech talk on video encoding.</summary>
</entry>
</feed>

5分鐘XML速成#

如果你已經(jīng)了解XML,可以跳過這一部分。

XML是一種描述層次結(jié)構(gòu)化數(shù)據(jù)的通用方法。XML文檔包含由起始和結(jié)束標(biāo)簽(tag)分隔的一個(gè)或多個(gè)元素(element)。以下也是一個(gè)完整的(雖然空洞)XML文件:

<foo> 
</foo>
這是foo元素的起始標(biāo)簽
這是foo元素對應(yīng)的結(jié)束標(biāo)簽。就如寫作、數(shù)學(xué)或者代碼中需要平衡括號(hào)一樣,每一個(gè)起始標(biāo)簽必須有對應(yīng)的結(jié)束標(biāo)簽來閉合(匹配)。

元素可以嵌套到任意層次。位于foo中的元素bar可以被稱作其子元素

<foo>
<bar></bar>
</foo>

XML文檔中的第一個(gè)元素叫做根元素(root element)。并且每份XML文檔只能有一個(gè)根元素。以下不是一個(gè)XML文檔,因?yàn)樗嬖趦蓚€(gè)“根元素”。

<foo></foo>
<bar></bar>

元素可以有其屬性(attribute),它們是一些名字-值(name-value)對。屬性由空格分隔列舉在元素的起始標(biāo)簽中。一個(gè)元素中屬性名不能重復(fù)。屬性值必須用引號(hào)包圍起來。單引號(hào)、雙引號(hào)都是可以。

<foo lang='en'> 
<bar id='papayawhip' lang="fr"></bar>
</foo>
foo元素有一個(gè)叫做lang的屬性。lang的值為en
bar元素則有兩個(gè)屬性,分別為idlang。其中lang屬性的值為fr。它不會(huì)與foo的那個(gè)屬性產(chǎn)生沖突。每個(gè)元素都其獨(dú)立的屬性集。

如果元素有多個(gè)屬性,書寫的順序并不重要。元素的屬性是一個(gè)無序的鍵-值對集,跟Python中的列表對象一樣。另外,元素中屬性的個(gè)數(shù)是沒有限制的。

元素可以有其文本內(nèi)容(text content)

<foo lang='en'>
<bar lang='fr'>PapayaWhip</bar>
</foo>

如果某一元素既沒有文本內(nèi)容,也沒有子元素,它也叫做空元素

<foo></foo>

表達(dá)空元素有一種簡潔的方法。通過在起始標(biāo)簽的尾部添加/字符,我們可以省略結(jié)束標(biāo)簽。上一個(gè)例子中的XML文檔可以寫成這樣:

<foo/>

就像Python函數(shù)可以在不同的模塊(modules)中聲明一樣,也可以在不同的名字空間(namespace)中聲明XML元素。XML文檔的名字空間通常看起來像URL。我們可以通過聲明xmlns來定義默認(rèn)名字空間。名字空間聲明跟元素屬性看起來很相似,但是它們的作用是不一樣的。

<feed xmlns='http://www.w3.org/2005/Atom'> 
<title>dive into mark</title>
</feed>
feed元素處在名字空間http://www.w3.org/2005/Atom中。
title元素也是。名字空間聲明不僅會(huì)作用于當(dāng)前聲明它的元素,還會(huì)影響到該元素的所有子元素。

也可以通過xmlns:prefix聲明來定義一個(gè)名字空間并取其名為prefix。然后該名字空間中的每個(gè)元素都必須顯式地使用這個(gè)前綴(prefix)來聲明。

<atom:feed xmlns:atom='http://www.w3.org/2005/Atom'> 
<atom:title>dive into mark</atom:title>
</atom:feed>
feed元素屬于名字空間http://www.w3.org/2005/Atom
title元素也在那個(gè)名字空間。

對于XML解析器而言,以上兩個(gè)XML文檔是一樣的。名字空間 + 元素名 = XML標(biāo)識(shí)。前綴只是用來引用名字空間的,所以對于解析器來說,這些前綴名(atom:)其實(shí)無關(guān)緊要的。名字空間相同,元素名相同,屬性(或者沒有屬性)相同,每個(gè)元素的文本內(nèi)容相同,則XML文檔相同。

最后,在根元素之前,字符編碼信息可以出現(xiàn)在XML文檔的第一行。(這里存在一個(gè)兩難的局面(catch-22),直觀上來說,解析XML文檔需要這些編碼信息,而這些信息又存在于XML文檔中,如果你對XML如何解決此問題有興趣,請參閱XML規(guī)范中 F 章節(jié)

<?xml version='1.0' encoding='utf-8'?>

現(xiàn)在我們已經(jīng)知道足夠多的XML知識(shí),可以開始探險(xiǎn)了!

Atom Feed的結(jié)構(gòu)#

想像一下網(wǎng)絡(luò)上的博客,或者互聯(lián)網(wǎng)上任何需要頻繁更新的網(wǎng)站,比如CNN.com。該站點(diǎn)有一個(gè)標(biāo)題(“CNN.com”),一個(gè)子標(biāo)題(“Breaking News, U.S., World, Weather, Entertainment & Video News”),包含上次更新的日期(“updated 12:43 p.m. EDT, Sat May 16, 2009”),還有在不同時(shí)期發(fā)布的文章的列表。每一篇文章也有自己的標(biāo)題,第一次發(fā)布的日期(如果曾經(jīng)修訂過或者改正過某個(gè)輸入錯(cuò)誤,或許也有一個(gè)上次更新的日期),并且每篇文章有自己唯一的URL。

Atom聚合格式被設(shè)計(jì)成可以包含所有這些信息的標(biāo)準(zhǔn)格式。我的博客無論在設(shè)計(jì),主題還是讀者上都與CNN.com大不相同,但是它們的基本結(jié)構(gòu)是相同的。CNN.com能做的事情,我的博客也能做…

每一個(gè)Atom訂閱都共享著一個(gè)根元素:即在名字空間http://www.w3.org/2005/Atom中的元素feed

跳過該代碼清單

http://www.w3.org/2005/Atom表示名字空間Atom。
每一個(gè)元素都可以包含xml:lang屬性,它用來聲明該元素及其子元素使用的語言。在當(dāng)前樣例中,xml:lang在根元素中被聲明了一次,也就意味著,整個(gè)feed都使用英文。

描述Atom feed自身的一些信息在根元素feed的子元素中被聲明。

跳過該代碼清單

該行表示這個(gè)feed的標(biāo)題為dive into mark
這一行表示子標(biāo)題為currently between addictions
每一個(gè)feed都要有一個(gè)全局唯一標(biāo)識(shí)符(globally unique identifier)。想要知道如何創(chuàng)建它,請查閱RFC 4151
表示當(dāng)前feed上次更新的時(shí)間為March 27, 2009, at 21:56 GMT。通常來說,它與最近一篇文章最后一次被修改的時(shí)間是一樣的。
事情開始變得有趣了…link元素沒有文本內(nèi)容,但是它有三個(gè)屬性:reltypehrefrel元素的值能告訴我們鏈接的類型;rel='alternate'表示這個(gè)鏈接指向當(dāng)前feed的另外一個(gè)版本。type='text/html'表示鏈接的目標(biāo)是一個(gè)HTML頁面。然后目標(biāo)地址在href屬性中指出。

現(xiàn)在我們知道這個(gè)feed上一更新是在on March 27, 2009,它是為一個(gè)叫做“dive into mark”的站點(diǎn)準(zhǔn)備的,并且站點(diǎn)的地址為http://diveintomark.org/

☞在有一些XML文檔中,元素的排列順序是有意義的,但是Atom feed中不需要這樣做。

feed級(jí)的元數(shù)據(jù)后邊就是最近文章的列表了。單獨(dú)的一篇文章就像這樣:

跳過該代碼清單

<entry>
<author>
<name>Mark</name>
<uri>http://diveintomark.org/</uri>
</author>
<title>Dive into history, 2009 edition</title>
<link rel='alternate' type='text/html'
href_cetemp='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
<id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
<updated>2009-03-27T21:56:07Z</updated>
<published>2009-03-27T17:20:42Z</published>
<category scheme='http://diveintomark.org' term='diveintopython'/>
<category scheme='http://diveintomark.org' term='docbook'/>
<category scheme='http://diveintomark.org' term='html'/>
<summary type='html'>Putting an entire chapter on one page sounds
bloated, but consider this &amp;mdash; my longest chapter so far
would be 75 printed pages, and it loads in under 5 seconds&amp;hellip;
On dialup.</summary>
</entry>
author元素指示文章的作者:一個(gè)叫做Mark的伙計(jì),并且我們可以在http://diveintomark.org/找到他的事跡。(這就像是feed元素里的備用鏈接,但是沒有規(guī)定一定要這樣。許多網(wǎng)絡(luò)日志由多個(gè)作者完成,他們都有自己的個(gè)人主頁。)
title元素給出這篇文章的標(biāo)題,即“Dive into history, 2009 edition”。
feed元素中的備用鏈接一樣,link元素給出這篇文章的HTML版本地址。
每個(gè)條目也像feed一樣,需要一個(gè)唯一的標(biāo)識(shí)。
每個(gè)條目有兩個(gè)日期與其相關(guān):第一次發(fā)布日期(published)和上次修改日期(updated)。
條目可以屬于任意多個(gè)類別。這篇文章被歸類到diveintopythondocbook,和html
summary元素中有這篇文章的概要性描述。(還有一個(gè)元素這里沒有展示出來,即content,我們可以把整篇文章的內(nèi)容都放在里邊。)當(dāng)前樣例中,summary元素含有一個(gè)Atom特有的type='html'屬性,它用來告知這份概要為HTML格式,而非純文本。這非常重要,因?yàn)楦乓獌?nèi)容中包含了HTML中特有的實(shí)體(&mdash;&hellip;),它們不應(yīng)該以純文本直接顯示,正確的形式應(yīng)該為“—”和“…”。
最后就是entry元素的結(jié)束標(biāo)記了,它指示文章元數(shù)據(jù)的結(jié)尾。

解析XML#

Python可以使用幾種不同的方式解析XML文檔。它包含了DOMSAX解析器,但是我們焦點(diǎn)將放在另外一個(gè)叫做ElementTree的庫上邊。

跳過該代碼清單

ElementTree屬于Python標(biāo)準(zhǔn)庫的一部分,它的位置為xml.etree.ElementTree
parse()函數(shù)是ElementTree庫的主要入口,它使用文件名或者流對象作為參數(shù)。parse()函數(shù)會(huì)立即解析完整個(gè)文檔。如果內(nèi)存資源緊張,也可以增量式地解析XML文檔
parse()函數(shù)會(huì)返回一個(gè)能代表整篇文檔的對象。這不是根元素。要獲得根元素的引用可以調(diào)用getroot()方法。
如預(yù)期的那樣,根元素即http://www.w3.org/2005/Atom名字空間中的feed。該字符串表示再次重申了非常重要的一點(diǎn):XML元素由名字空間和標(biāo)簽名(也稱作本地名(local name))組成。這篇文檔中的每個(gè)元素都在名字空間Atom中,所以根元素被表示為{http://www.w3.org/2005/Atom}feed

☞ElementTree使用{namespace}localname來表達(dá)XML元素。我們將會(huì)在ElementTree的API中多次見到這種形式。

元素即列表#

在ElementTree API中,元素的行為就像列表一樣。列表中的項(xiàng)即該元素的子元素。

跳過該代碼清單

# continued from the previous example >>> root.tag '{http://www.w3.org/2005/Atom}feed' >>> len(root) 8 >>> for child in root: ... print(child) ... <Element {http://www.w3.org/2005/Atom}title at e2b5d0> <Element {http://www.w3.org/2005/Atom}subtitle at e2b4e0> <Element {http://www.w3.org/2005/Atom}id at e2b6c0> <Element {http://www.w3.org/2005/Atom}updated at e2b6f0> <Element {http://www.w3.org/2005/Atom}link at e2b4b0> <Element {http://www.w3.org/2005/Atom}entry at e2b720> <Element {http://www.w3.org/2005/Atom}entry at e2b510> <Element {http://www.w3.org/2005/Atom}entry at e2b750>
緊接前一例子,根元素為{http://www.w3.org/2005/Atom}feed
根元素的“長度”即子元素的個(gè)數(shù)。
我們可以像使用迭代器一樣來遍歷其子元素。
從輸出可以看到,根元素總共有8個(gè)子元素:所有feed級(jí)的元數(shù)據(jù)(titlesubtitleidupdatedlink),還有緊接著的三個(gè)entry元素。

也許你已經(jīng)注意到了,但我還是想要指出來:該列表只包含直接子元素。每一個(gè)entry元素都有其子元素,但是并沒有包括在這個(gè)列表中。這些子元素本可以包括在entry元素的列表中,但是確實(shí)不屬于feed的子元素。但是,無論這些元素嵌套的層次有多深,總是有辦法定位到它們的;在這章的后續(xù)部分我們會(huì)介紹兩種方法。

屬性即字典#

XML不只是元素的集合;每一個(gè)元素還有其屬性集。一旦獲取了某個(gè)元素的引用,我們可以像操作Python的字典一樣輕松獲取到其屬性。

跳過該代碼清單

# continuing from the previous example >>> root.attrib {'{http://www.w3.org/XML/1998/namespace}lang': 'en'} >>> root[4] <Element {http://www.w3.org/2005/Atom}link at e181b0> >>> root[4].attrib {'href': 'http://diveintomark.org/',
'type': 'text/html',
'rel': 'alternate'}
>>> root[3] <Element {http://www.w3.org/2005/Atom}updated at e2b4e0> >>> root[3].attrib {}
attrib是一個(gè)代表元素屬性的字典。這個(gè)地方原來的標(biāo)記語言是這樣描述的:<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>。前綴xml:指示一個(gè)內(nèi)置的名字空間,每一個(gè)XML不需要聲明就可以使用它。
第五個(gè)子元素 — 以0為起始的列表中即[4] — 為元素link
link元素有三個(gè)屬性:hreftype,和rel
第四個(gè)子元素 — [3] — 為updated
元素updated沒有子元素,所以.attrib是一個(gè)空的字典對象。

在XML文檔中查找結(jié)點(diǎn)#

到目前為止,我們已經(jīng)“自頂向下“地從根元素開始,一直到其子元素,走完了整個(gè)文檔。但是許多情況下我們需要找到XML中特定的元素。Etree也能完成這項(xiàng)工作。

跳過該代碼清單

>>> import xml.etree.ElementTree as etree >>> tree = etree.parse('examples/feed.xml') >>> root = tree.getroot() >>> root.findall('{http://www.w3.org/2005/Atom}entry') [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>, <Element {http://www.w3.org/2005/Atom}entry at e2b510>, <Element {http://www.w3.org/2005/Atom}entry at e2b540>] >>> root.tag '{http://www.w3.org/2005/Atom}feed' >>> root.findall('{http://www.w3.org/2005/Atom}feed') [] >>> root.findall('{http://www.w3.org/2005/Atom}author') []
findfall()方法查找匹配特定格式的子元素。(關(guān)于查詢的格式稍后會(huì)講到。)
每個(gè)元素 — 包括根元素及其子元素 — 都有findall()方法。它會(huì)找到所有匹配的子元素。但是為什么沒有看到任何結(jié)果呢?也許不太明顯,這個(gè)查詢只會(huì)搜索其子元素。由于根元素feed中不存在任何叫做feed的子元素,所以查詢的結(jié)果為一個(gè)空的列表。
這個(gè)結(jié)果也許也在你的意料之外。在這篇文檔中確實(shí)存在author元素;事實(shí)上總共有三個(gè)(每個(gè)entry元素中都有一個(gè))。但是那些author元素不是根元素的直接子元素。我們可以在任意嵌套層次中查找author元素,但是查詢的格式會(huì)有些不同。

跳過該代碼清單

>>> tree.findall('{http://www.w3.org/2005/Atom}entry') [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>, <Element {http://www.w3.org/2005/Atom}entry at e2b510>, <Element {http://www.w3.org/2005/Atom}entry at e2b540>] >>> tree.findall('{http://www.w3.org/2005/Atom}author') []
為了方便,對象tree(調(diào)用etree.parse()的返回值)中的一些方法是根元素中這些方法的鏡像。在這里,如果調(diào)用tree.getroot().findall(),則返回值是一樣的。
也許有些意外,這個(gè)查詢請求也沒有找到文檔中的author元素。為什么沒有呢?因?yàn)樗皇?code>tree.getroot().findall('{http://www.w3.org/2005/Atom}author')的一種簡潔表示,即“查詢所有是根元素的子元素的author”。因?yàn)檫@些authorentry元素的子元素,所以查詢沒有找到任何匹配的。

find()方法用來返回第一個(gè)匹配到的元素。當(dāng)我們認(rèn)為只會(huì)有一個(gè)匹配,或者有多個(gè)匹配但我們只關(guān)心第一個(gè)的時(shí)候,這個(gè)方法是很有用的。

跳過該代碼清單

在前一樣例中已經(jīng)看到。這一句返回所有的atom:entry元素。
find()方法使用ElementTree作為參數(shù),返回第一個(gè)匹配到的元素。
entries[0]中沒有叫做foo的元素,所以返回值為None

☞可逮住你了,在這里find()方法非常容易被誤解。在布爾上下文中,如果ElementTree元素對象不包含子元素,其值則會(huì)被認(rèn)為是False如果len(element)等于0)。這就意味著if element.find('...')并非在測試是否find()方法找到了匹配項(xiàng);這條語句是在測試匹配到的元素是否包含子元素!想要測試find()方法是否返回了一個(gè)元素,則需使用if element.find('...') is not None

可以在所有派生(descendant)元素中搜索,任意嵌套層次的子元素,孫子元素等…

跳過該代碼清單

>>> all_links = tree.findall('//{http://www.w3.org/2005/Atom}link') >>> all_links [<Element {http://www.w3.org/2005/Atom}link at e181b0>, <Element {http://www.w3.org/2005/Atom}link at e2b570>, <Element {http://www.w3.org/2005/Atom}link at e2b480>, <Element {http://www.w3.org/2005/Atom}link at e2b5a0>] >>> all_links[0].attrib {'href': 'http://diveintomark.org/',
'type': 'text/html',
'rel': 'alternate'}
>>> all_links[1].attrib {'href': 'http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition',
'type': 'text/html',
'rel': 'alternate'}
>>> all_links[2].attrib {'href': 'http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress',
'type': 'text/html',
'rel': 'alternate'}
>>> all_links[3].attrib {'href': 'http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats',
'type': 'text/html',
'rel': 'alternate'}
//{http://www.w3.org/2005/Atom}link與前一樣例很相似,除了開頭的兩條斜線。這兩條斜線告訴findall()方法“不要只在直接子元素中查找;查找的范圍可以是任意嵌套層次”。
查詢到的第一個(gè)結(jié)果根元素的直接子元素。從它的屬性中可以看出,它是一個(gè)指向該feed的HTML版本的備用鏈接。
其他的三個(gè)結(jié)果分別是低一級(jí)的備用鏈接。每一個(gè)entry都有單獨(dú)一個(gè)link子元素,由于在查詢語句前的兩條斜線的作用,我們也能定位到他們。

總的來說,ElementTree的findall()方法是其一個(gè)非常強(qiáng)大的特性,但是它的查詢語言卻讓人有些出乎意料。官方描述它為“有限的XPath支持。”XPath是一種用于查詢XML文檔的W3C標(biāo)準(zhǔn)。對于基礎(chǔ)地查詢來說,ElementTree與XPath語法上足夠相似,但是如果已經(jīng)會(huì)XPath的話,它們之間的差異可能會(huì)使你感到不快。現(xiàn)在,我們來看一看另外一個(gè)第三方XML庫,它擴(kuò)展了ElementTree的API以提供對XPath的全面支持。

深入lxml#

lxml是一個(gè)開源的第三方庫,以流行的libxml2 解析器為基礎(chǔ)開發(fā)。提供了與ElementTree完全兼容的API,并且擴(kuò)展它以提供了對XPath 1.0的全面支持,以及改進(jìn)了一些其他精巧的細(xì)節(jié)。提供Windows的安裝程序;Linux用戶推薦使用特定發(fā)行版自帶的工具比如yum或者apt-get從它們的程序庫中安裝預(yù)編譯好了的二進(jìn)制文件。要不然,你就得手工安裝他們了。

跳過該代碼清單

>>> from lxml import etree >>> tree = etree.parse('examples/feed.xml') >>> root = tree.getroot() >>> root.findall('{http://www.w3.org/2005/Atom}entry') [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>, <Element {http://www.w3.org/2005/Atom}entry at e2b510>, <Element {http://www.w3.org/2005/Atom}entry at e2b540>]
導(dǎo)入lxml以后,可以發(fā)現(xiàn)它與內(nèi)置的ElementTree庫提供相同的API
parse()函數(shù):與ElementTree相同。
getroot()方法:相同。
findall()方法:完全相同。

對于大型的XML文檔,lxml明顯比內(nèi)置的ElementTree快了許多。如果現(xiàn)在只用到了ElementTree的API,并且想要使用其最快的實(shí)現(xiàn)(implementation),我們可以嘗試導(dǎo)入lxml,并且將內(nèi)置的ElementTree作為備用。

try:
from lxml import etree
except ImportError:
import xml.etree.ElementTree as etree

但是lxml不只是一個(gè)更快速的ElementTree。它的findall()方法能夠支持更加復(fù)雜的表達(dá)式。

跳過該代碼清單

>>> import lxml.etree >>> tree = lxml.etree.parse('examples/feed.xml') >>> tree.findall('//{http://www.w3.org/2005/Atom}*[@href]') [<Element {http://www.w3.org/2005/Atom}link at eeb8a0>, <Element {http://www.w3.org/2005/Atom}link at eeb990>, <Element {http://www.w3.org/2005/Atom}link at eeb960>, <Element {http://www.w3.org/2005/Atom}link at eeb9c0>] >>> tree.findall("http://{http://www.w3.org/2005/Atom}*[@) [<Element {http://www.w3.org/2005/Atom}link at eeb930>] >>> NS = '{http://www.w3.org/2005/Atom}' >>> tree.findall('//{NS}author[{NS}uri]'.format(NS=NS)) [<Element {http://www.w3.org/2005/Atom}author at eeba80>, <Element {http://www.w3.org/2005/Atom}author at eebba0>]
在這個(gè)樣例中,我使用了import lxml.etree(而非from lxml import etree),以強(qiáng)調(diào)這些特性只限于lxml
這一句在整個(gè)文檔范圍內(nèi)搜索名字空間Atom中具有href屬性的所有元素。在查詢語句開頭的//表示“搜索的范圍為整個(gè)文檔(不只是根元素的子元素)。” {http://www.w3.org/2005/Atom}指示“搜索范圍僅在名字空間Atom中。” * 表示“任意本地名(local name)的元素。” [@href]表示“含有href屬性。”
該查詢找出所有包含href屬性并且其值為http://diveintomark.org/的Atom元素。
在簡單的字符串格式化后(要不然這條復(fù)合查詢語句會(huì)變得特別長),它搜索名字空間Atom中包含uri元素作為子元素的author元素。該條語句只返回了第一個(gè)和第二個(gè)entry元素中的author元素。最后一個(gè)entry元素中的author只包含有name屬性,沒有uri

仍然不夠用?lxml也集成了對任意XPath 1.0表達(dá)式的支持。我們不會(huì)深入講解XPath的語法;那可能需要一整本書!但是我會(huì)給你展示它是如何集成到lxml去的。

跳過該代碼清單

>>> import lxml.etree >>> tree = lxml.etree.parse('examples/feed.xml') >>> NSMAP = {'atom': 'http://www.w3.org/2005/Atom'} >>> entries = tree.xpath("http://atom:category[@term='accessibility']/..", ... namespaces=NSMAP) >>> entries [<Element {http://www.w3.org/2005/Atom}entry at e2b630>] >>> entry = entries[0] >>> entry.xpath('./atom:title/text()', namespaces=NSMAP) ['Accessibility is a harsh mistress']
要查詢名字空間中的元素,首先需要定義一個(gè)名字空間前綴映射。它就是一個(gè)Python字典對象。
這就是一個(gè)XPath查詢請求。這個(gè)XPath表達(dá)式目的在于搜索category元素,并且該元素包含有值為accessibilityterm屬性。但是那并不是查詢的結(jié)果。請看查詢字符串的尾端;是否注意到了/..這一塊?它的意思是,“然后返回已經(jīng)找到的category元素的父元素。”所以這條XPath查詢語句會(huì)找到所有包含<category term='accessibility'>作為子元素的條目。
xpath()函數(shù)返回一個(gè)ElementTree對象列表。在這篇文檔中,只有一個(gè)category元素,并且它的term屬性值為accessibility
XPath表達(dá)式并不總是會(huì)返回一個(gè)元素列表。技術(shù)上說,一個(gè)解析了的XML文檔的DOM模型并不包含元素;它只包含結(jié)點(diǎn)(node)。依據(jù)它們的類型,結(jié)點(diǎn)可以是元素,屬性,甚至是文本內(nèi)容。XPath查詢的結(jié)果是一個(gè)結(jié)點(diǎn)列表。當(dāng)前查詢返回一個(gè)文本結(jié)點(diǎn)列表:title元素(atom:title)的文本內(nèi)容(text()),并且title元素必須是當(dāng)前元素的子元素(./)。

生成XML#

Python對XML的支持不只限于解析已存在的文檔。我們也可以從頭來創(chuàng)建XML文檔。

跳過該代碼清單

實(shí)例化Element類來創(chuàng)建一個(gè)新元素。可以將元素的名字(名字空間 + 本地名)作為其第一個(gè)參數(shù)。當(dāng)前語句在Atom名字空間中創(chuàng)建一個(gè)feed元素。它將會(huì)成為我們文檔的根元素。
將屬性名和值構(gòu)成的字典對象傳遞給attrib參數(shù),這樣就可以給新創(chuàng)建的元素添加屬性。請注意,屬性名應(yīng)該使用標(biāo)準(zhǔn)的ElementTree格式,{namespace}localname
在任何時(shí)候,我們可以使用ElementTree的tostring()函數(shù)序列化任意元素(還有它的子元素)。

這種序列化結(jié)果有使你感到意外嗎?技術(shù)上說,ElementTree使用的序列化方法是精確的,但卻不是最理想的。在本章開頭給出的XML樣例文檔中定義了一個(gè)默認(rèn)名字空間(default namespace)(xmlns='http://www.w3.org/2005/Atom')。對于每個(gè)元素都在同一個(gè)名字空間中的文檔 — 比如Atom feeds — 定義默認(rèn)的名字空間非常有用,因?yàn)橹恍枰暶饕淮蚊挚臻g,然后在聲明每個(gè)元素的時(shí)候只需要使用其本地名即可(<feed><link><entry>)。除非想要定義另外一個(gè)名字空間中的元素,否則沒有必要使用前綴。

對于XML解析器來說,它不會(huì)“注意”到使用默認(rèn)名字空間和使用前綴名字空間的XML文檔之間有什么不同。當(dāng)前序列化結(jié)果的DOM為:

<ns0:feed xmlns:ns0='http://www.w3.org/2005/Atom' xml:lang='en'/>

與下列序列化的DOM是一模一樣的:

<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'/>

實(shí)際上唯一不同的只是第二個(gè)序列化短了幾個(gè)字符長度。如果我們改動(dòng)整個(gè)樣例feed,使每一個(gè)起始和結(jié)束標(biāo)簽都有一個(gè)ns0:前綴,這將為每個(gè)起始標(biāo)簽增加 4 個(gè)字符 × 79 個(gè)標(biāo)簽 + 4 個(gè)名字空間聲明本身用到的字符,總共320個(gè)字符。假設(shè)我們使用UTF-8編碼,那將是320個(gè)額外的字節(jié)。(使用gzip壓縮以后,大小可以降到21個(gè)字節(jié),但是,21個(gè)字節(jié)也是字節(jié)。)也許對個(gè)人來說這算不了什么,但是對于像Atom feed這樣的東西,只要稍有改變就有可能被下載上千次,每一個(gè)請求節(jié)約的幾個(gè)字節(jié)就會(huì)迅速累加起來。

內(nèi)置的ElementTree庫沒有提供細(xì)粒度地對序列化時(shí)名字空間內(nèi)的元素的控制,但是lxml有這樣的功能。

跳過該代碼清單

>>> import lxml.etree >>> NSMAP = {None: 'http://www.w3.org/2005/Atom'} >>> new_feed = lxml.etree.Element('feed', nsmap=NSMAP) >>> print(lxml.etree.tounicode(new_feed)) <feed xmlns='http://www.w3.org/2005/Atom'/> >>> new_feed.set('{http://www.w3.org/XML/1998/namespace}lang', 'en') >>> print(lxml.etree.tounicode(new_feed)) <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'/>
首先,定義一個(gè)用于名字空間映射的字典對象。其值為名字空間;字典中的鍵即為所需要的前綴。使用None作為前綴來定義默認(rèn)的名字空間。
現(xiàn)在我們可以在創(chuàng)建元素的時(shí)候,給lxml專有的nsmap參數(shù)傳值,并且lxml會(huì)參照我們所定義的名字空間前綴。
如所預(yù)期的那樣,該序列化使用Atom作為默認(rèn)的名字空間,并且在聲明feed元素的時(shí)候沒有使用名字空間前綴。
啊噢… 我們忘了加上xml:lang屬性。我們可以使用set()方法來隨時(shí)給元素添加所需屬性。該方法使用兩個(gè)參數(shù):標(biāo)準(zhǔn)ElementTree格式的屬性名,然后,屬性值。(該方法不是lxml特有的。在該樣例中,只有nsmap參數(shù)是lxml特有的,它用來控制序列化輸出時(shí)名字空間的前綴。)

難道每個(gè)XML文檔只能有一個(gè)元素嗎?當(dāng)然不了。我們可以創(chuàng)建子元素。

跳過該代碼清單

>>> title = lxml.etree.SubElement(new_feed, 'title', ... attrib={'type':'html'}) >>> print(lxml.etree.tounicode(new_feed)) <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'><title type='html'/></feed> >>> title.text = 'dive into &hellip;' >>> print(lxml.etree.tounicode(new_feed)) <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'><title type='html'>dive into &amp;hellip;</title></feed> >>> print(lxml.etree.tounicode(new_feed, pretty_print=True)) <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
<title type='html'>dive into&amp;hellip;</title>
</feed>
給已有元素創(chuàng)建子元素,我們需要實(shí)例化SubElement類。它只要求兩個(gè)參數(shù),父元素(即該樣例中的new_feed)和子元素的名字。由于該子元素會(huì)從父元素那兒繼承名字空間的映射關(guān)系,所以這里不需要再聲明名字空間前綴。
我們也可以傳遞屬性字典給它。字典的鍵即屬性名;值為屬性的值。
如預(yù)期的那樣,新創(chuàng)建的title元素在Atom名字空間中,并且它作為子元素插入到feed元素中。由于title元素沒有文件內(nèi)容,也沒有其子元素,所以lxml將其序列化為一個(gè)空元素(使用/>)。
設(shè)定元素的文本內(nèi)容,只需要設(shè)定其.text屬性。
當(dāng)前title元素序列化的時(shí)候就使用了其文本內(nèi)容。任何包含了<或者&符號(hào)的內(nèi)容在序列化的時(shí)候需要被轉(zhuǎn)義。lxml會(huì)自動(dòng)處理轉(zhuǎn)義。

☞你也許也想要看一看xmlwitch,它也是用來生成XML的另外一個(gè)第三方庫。它大量地使用了with語句來使生成的XML代碼更具可讀性。

解析破損的XML#

XML規(guī)范文檔中指出,要求所有遵循XML規(guī)范的解析器使用“嚴(yán)厲的(draconian)錯(cuò)誤處理”。即,當(dāng)它們在XML文檔中檢測到任何編排良好性(wellformedness)錯(cuò)誤的時(shí)候,應(yīng)當(dāng)立即停止解析。編排良好性錯(cuò)誤包括不匹配的起始和結(jié)束標(biāo)簽,未定義的實(shí)體(entity),非法的Unicode字符,還有一些只有內(nèi)行才懂的規(guī)則(esoteric rules)。這與其他的常見格式,比如HTML,形成了鮮明的對比 — 即使忘記了封閉HTML標(biāo)簽,或者在屬性值中忘了轉(zhuǎn)義&字符,我們的瀏覽器也不會(huì)停止渲染一個(gè)Web頁面。(通常大家認(rèn)為HTML沒有錯(cuò)誤處理機(jī)制,這是一個(gè)常見的誤解。HTML的錯(cuò)誤處理實(shí)際上被很好的定義了,但是它比“遇見第一個(gè)錯(cuò)誤即停止”這種機(jī)制要復(fù)雜得多。)

一些人(包括我自己)認(rèn)為XML的設(shè)計(jì)者強(qiáng)制實(shí)行這種嚴(yán)格的錯(cuò)誤處理本身是一個(gè)失誤。請不要誤解我;我當(dāng)然能看到簡化錯(cuò)誤處理機(jī)制的優(yōu)勢。但是在現(xiàn)實(shí)中,“編排良好性”這種構(gòu)想比乍聽上去更加復(fù)雜,特別是對XML(比如Atom feeds)這種發(fā)布在網(wǎng)絡(luò)上,通過HTTP傳播的文檔。早在1997年XML就標(biāo)準(zhǔn)化了這種嚴(yán)厲的錯(cuò)誤處理,盡管XML已經(jīng)非常成熟,研究一直表明,網(wǎng)絡(luò)上相當(dāng)一部分的Atom feeds仍然存在著編排完整性錯(cuò)誤。

所以,從理論上和實(shí)際應(yīng)用兩種角度來看,我有理由“不惜任何代價(jià)”來解析XML文檔,即,當(dāng)遇到編排良好性錯(cuò)誤時(shí),不會(huì)中斷解析操作。如果你認(rèn)為你也需要這樣做,lxml可以助你一臂之力。

以下是一個(gè)破損的XML文檔的片斷。其中的編排良好性錯(cuò)誤已經(jīng)被高亮標(biāo)出來了。

<?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
<title>dive into &hellip;</title>
...
</feed>

因?yàn)閷?shí)體&hellip;并沒有在XML中被定義,所以這算作一個(gè)錯(cuò)誤。(它在HTML中被定義。)如果我們嘗試使用默認(rèn)的設(shè)置來解析該破損的feed,lxml會(huì)因?yàn)檫@個(gè)未定義的實(shí)體而停下來。

>>> import lxml.etree >>> tree = lxml.etree.parse('examples/feed-broken.xml') Traceback (most recent call last):   File "<stdin>", line 1, in <module>   File "lxml.etree.pyx", line 2693, in lxml.etree.parse (src/lxml/lxml.etree.c:52591)   File "parser.pxi", line 1478, in lxml.etree._parseDocument (src/lxml/lxml.etree.c:75665)   File "parser.pxi", line 1507, in lxml.etree._parseDocumentFromURL (src/lxml/lxml.etree.c:75993)   File "parser.pxi", line 1407, in lxml.etree._parseDocFromFile (src/lxml/lxml.etree.c:75002)   File "parser.pxi", line 965, in lxml.etree._BaseParser._parseDocFromFile (src/lxml/lxml.etree.c:72023)   File "parser.pxi", line 539, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:67830)   File "parser.pxi", line 625, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:68877)   File "parser.pxi", line 565, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:68125) lxml.etree.XMLSyntaxError: Entity 'hellip' not defined, line 3, column 28

為了解析該破損的XML文檔,忽略它的編排良好性錯(cuò)誤,我們需要?jiǎng)?chuàng)建一個(gè)自定義的XML解析器。

跳過該代碼清單

>>> parser = lxml.etree.XMLParser(recover=True) >>> tree = lxml.etree.parse('examples/feed-broken.xml', parser) >>> parser.error_log examples/feed-broken.xml:3:28:FATAL:PARSER:ERR_UNDECLARED_ENTITY: Entity 'hellip' not defined >>> tree.findall('{http://www.w3.org/2005/Atom}title') [<Element {http://www.w3.org/2005/Atom}title at ead510>] >>> title = tree.findall('{http://www.w3.org/2005/Atom}title')[0] >>> title.text 'dive into ' >>> print(lxml.etree.tounicode(tree.getroot())) <feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
<title>dive into </title>
.
. [rest of serialization snipped for brevity]
.
實(shí)例化lxml.etree.XMLParser類來創(chuàng)建一個(gè)自定義的解析器。它可以使用許多不同的命名參數(shù)。在此,我們感興趣的為recover參數(shù)。當(dāng)它的值被設(shè)為TrueXML解析器會(huì)盡力嘗試從編排良好性錯(cuò)誤中“恢復(fù)”。
為使用自定的解析器來處理XML文檔,將對象parser作為第二個(gè)參數(shù)傳遞給parse()函數(shù)。注意,lxml沒有因?yàn)槟莻€(gè)未定義的&hellip;實(shí)體而拋出異常。
解析器會(huì)記錄它所遇到的所有編排良好性錯(cuò)誤。(無論它是否被設(shè)置為需要從錯(cuò)誤中恢復(fù),這個(gè)記錄總會(huì)存在。)
由于不知道如果處理該未定義的&hellip;實(shí)體,解析器默認(rèn)會(huì)將其省略掉。title元素的文本內(nèi)容變成了'dive into '
從序列化的結(jié)果可以看出,實(shí)體&hellip;并沒有被移到其他地方去;它就是被省略了。

在此,必須反復(fù)強(qiáng)調(diào),這種“可恢復(fù)的”XML解析器沒有互用性(interoperability)保證。另一個(gè)不同的解析器可能就會(huì)認(rèn)為&hellip;來自HTML,然后將其替換為&amp;hellip;。這樣“更好”嗎?也許吧。這樣“更正確”嗎?不,兩種處理方法都不正確。正確的行為(根據(jù)XML規(guī)范)應(yīng)該是終止解析操作。如果你已經(jīng)決定不按規(guī)范來,你得自己負(fù)責(zé)。

進(jìn)一步閱讀#

© 2001–9 Mark Pilgrim

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            中文一区字幕| 日韩网站在线| 久久国产精品色婷婷| 亚洲一区二区欧美日韩| 一本久道久久综合中文字幕| 亚洲日本成人| 亚洲黄色免费网站| 亚洲高清三级视频| 亚洲第一黄网| 1024成人| 亚洲国内精品| 亚洲日本视频| 亚洲肉体裸体xxxx137| 最近看过的日韩成人| 91久久久久久久久| 亚洲人在线视频| 999在线观看精品免费不卡网站| 亚洲精品久久久蜜桃| 亚洲精品麻豆| 一区二区三区视频在线观看| 亚洲视频在线免费观看| 亚洲永久精品大片| 欧美与黑人午夜性猛交久久久| 亚洲影院在线| 性视频1819p久久| 午夜精品视频一区| 亚洲一区二区毛片| 性欧美大战久久久久久久免费观看 | 欧美韩日亚洲| 欧美激情第10页| 欧美日韩在线高清| 国产精品嫩草影院一区二区| 国产午夜精品一区二区三区视频| 国产在线不卡精品| 国模大胆一区二区三区| 激情文学综合丁香| 日韩视频免费观看高清在线视频| 一区二区三区毛片| 午夜免费久久久久| 老司机精品久久| 亚洲国产合集| 99精品欧美一区二区三区综合在线 | 国内精品99| **欧美日韩vr在线| 国产精品99久久久久久有的能看| 亚洲欧美第一页| 午夜精品美女久久久久av福利| 久久国产夜色精品鲁鲁99| 你懂的网址国产 欧美| 亚洲黄色在线观看| 亚洲一区在线播放| 久久精品在线播放| 欧美日韩精品一区二区天天拍小说| 国产精品久久久久久久久婷婷| 国内欧美视频一区二区| 亚洲美女啪啪| 久久爱www| 亚洲欧洲另类| 香蕉尹人综合在线观看| 欧美大片免费| 国产欧美日本一区二区三区| 在线观看国产成人av片| 99成人免费视频| 欧美有码视频| 欧美国产日韩免费| 亚洲一区免费在线观看| 欧美成人一区二区三区| 国产精品一区二区久久国产| 亚洲人成在线观看一区二区| 欧美在线观看www| 亚洲欧洲综合另类| 久久99在线观看| 欧美日韩国产麻豆| 在线欧美小视频| 午夜精品一区二区三区电影天堂 | 亚洲精选视频免费看| 久久国产加勒比精品无码| 亚洲肉体裸体xxxx137| 久久精品女人| 欧美三级日本三级少妇99| 国产又爽又黄的激情精品视频| 一区二区三区高清在线观看| 免费久久99精品国产| 亚洲欧美激情四射在线日 | 亚洲第一区在线观看| 欧美亚洲色图校园春色| 欧美日韩一区二区三| 亚洲福利久久| 久久久精品2019中文字幕神马| 99精品视频免费全部在线| 免费欧美高清视频| 精品盗摄一区二区三区| 久久er99精品| 亚洲视频一区二区| 欧美片在线观看| 亚洲精品偷拍| 欧美成人dvd在线视频| 午夜亚洲一区| 欧美日韩国产免费观看| 亚洲精品久久久久久下一站| 久久免费黄色| 香港成人在线视频| 国产精品一区二区你懂得 | 国产精品视频99| 亚洲图片欧洲图片av| 亚洲高清激情| 欧美大香线蕉线伊人久久国产精品| 狠狠色噜噜狠狠色综合久| 久久久国产精品一区二区中文| 亚洲一级黄色av| 国产精品国产三级国产aⅴ9色| 一区二区三区日韩精品| 亚洲美女在线看| 欧美日韩国产片| 中文欧美在线视频| 在线视频你懂得一区| 欧美视频在线免费| 亚洲欧美日韩在线播放| 亚洲午夜视频在线观看| 国产精品一区毛片| 久久精品中文字幕免费mv| 欧美一级电影久久| 国产在线欧美日韩| 男同欧美伦乱| 免费观看日韩| aa级大片欧美三级| 99热精品在线| 国产伦精品一区二区三区高清版 | 亚洲日本中文字幕免费在线不卡| 毛片一区二区三区| 日韩午夜在线| 一区二区三区成人精品| 国产精品视频免费观看www| 欧美一区二区视频在线观看2020 | 亚洲免费观看视频| 99国产精品久久久久久久成人热| 国产精品福利在线| 久久黄色小说| 久久夜色精品国产欧美乱极品| 亚洲精美视频| 日韩一区二区久久| 国产乱子伦一区二区三区国色天香 | 欧美日韩在线一区二区| 午夜精品久久久久久久99黑人| 一区二区三区色| 国产欧美丝祙| 欧美在线啊v| 另类酷文…触手系列精品集v1小说| 亚洲精品看片| 亚洲综合电影| 亚洲国产aⅴ天堂久久| 一本色道久久综合一区| 国产主播一区二区| 亚洲人成网站影音先锋播放| 国产精品女主播在线观看| 快she精品国产999| 欧美日韩你懂的| 久久久www| 欧美日韩八区| 久久乐国产精品| 欧美日韩国产一区二区| 久久gogo国模裸体人体| 免费一级欧美片在线播放| 午夜亚洲性色视频| 免费日韩av电影| 亚洲欧美综合| 欧美成人免费网| 久久国产精品久久久久久电车| 欧美va天堂在线| 久久不射电影网| 欧美精品乱码久久久久久按摩| 欧美尤物巨大精品爽| 欧美国产日本高清在线| 久久激情五月婷婷| 免费日韩一区二区| 欧美亚洲视频| 欧美理论电影在线播放| 久久久中精品2020中文| 欧美视频二区| 亚洲国产成人精品久久久国产成人一区| 国产精品区二区三区日本 | 亚洲精品资源美女情侣酒店| 亚洲欧美日韩中文播放| 一区二区三区欧美日韩| 久久免费视频这里只有精品| 先锋影音国产精品| 欧美日本高清视频| 欧美freesex8一10精品| 国产精品亚洲一区| 亚洲精品久久久蜜桃| 亚洲美女免费精品视频在线观看| 久久亚洲综合网| 免费在线欧美视频| 激情久久综合| 久久九九有精品国产23| 久久久久久自在自线| 国产日韩在线播放| 欧美在线啊v一区| 欧美一区二区三区四区视频| 国产精品夜夜夜|