参考書:「Python3Webクローラー開発の実際の戦闘」
XPathについて知る
6月6日の朝、突然、評価結果が合格したという通知を受け取り、RDCビッグデータチームのメンバーになったことに驚きました。2か月の思考と突破口は、流行の間に家で勉強した時間の忘れられない思い出の1つです。プログレスバーの%5からすべてのタスクの要件に近い%90まで、ずっと下まで、それはアクションと意志にも依存します。
審査月間は、スタジオの日々がしばらく終わります。試験が終わった今日、RDCサマースタディ期間(Xiaoxiao996)が始まります。先に述べたように、ビッグデータビジネスの主な方向性はアルゴリズムと開発です。夏休みの勉強は主に前者にありますが、この一ヶ月以上の勉強を通して、自分の興味や方向性を確信するだけでなく、できるだけ早くチームワーク能力を高めて仕事ができることを願っています。もっと強く。
今日では、前回のクローラの基本の簡単な審査の後、私が始めた学習XPathの1を。
前書き
以前に実装したクローラーがページ情報を抽出する場合、基本的に正規表現を使用しますが、これはさらに
面倒であり、何か問題があるとマッチングが失敗する可能性があるため、正規表現を使用してページ情報を抽出するのは多かれ少なかれです間違っています。便利です。
Webノード(タグ)の場合、ID、クラス、またはその他の属性を定義できますが、ノード階層間でも、XPathを介したWebページで、CSSセレクターを使用して1つ以上のノードを検索し、ページ内で次を使用します。 XPath、ノードを抽出するためのCSSセレクター、そして対応するメソッドを呼び出してその本体のコンテンツまたは属性を取得すると、必要な情報を抽出できます。
XPathは、あるXMLパス言語、の略でXMLパス言語XML文書内の情報を検索するための言語です、。もともとはXMLドキュメントの検索に使用されていましたが、HTMLドキュメント2の検索にも適用されます。したがって、クローラーを実行する場合、XPathを使用して対応する情報を抽出できます。
一般的な解析ライブラリには、lxml(XMLとHTMLを迅速かつ柔軟に処理でき、Webクロールにも使用できるPythonで記述されたライブラリ)、Beautiful Soup、pyqueryなどがあります。
共通のルール
インスタンスの紹介
from lxml import etree
# 导入 lxml 库的 etree 模块
# 然后声明一段 HTML 文本,调用 HTML类进行初始化,成功构造一个 XPath解析对象。
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))
# HTML 文本中最后一个 li 节点没有闭合,但是 etree 模块可以自动修正 HTML 文本。
# 调用 tostring() 方法即可输出修正后的 HTML 代码,但结果是 bytes 类型
# 可以用 decode() 方法将其转化为 str 类型
# 结果如下:
<html><body><div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</li></ul>
</div>
</body></html>
# 经过处理后,li 节点标签被补全,并且还自动添加了 body、html 节点
# 还可以直接读取文本文件进行解析:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
ノード
//で始まるXPathルールを使用して、要件を満たすすべてのノードを選択します。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
# * 代表匹配所有节点,返回的结果是一个列表,每个元素都是一个 Element 类型,后跟节点名称。
# 运行结果:
"""
[<Element html at 0x1d6610ebe08>, <Element body at 0x1d6610ebf08>,
<Element div at 0x1d6610ebf48>, <Element ul at 0x1d6610ebf88>,
<Element li at 0x1d6610ebfc8>, <Element a at 0x1d661115088>,
<Element li at 0x1d6611150c8>, <Element a at 0x1d661115108>,
<Element li at 0x1d661115148>, <Element a at 0x1d661115048>,
<Element li at 0x1d661115188>, <Element a at 0x1d6611151c8>,
<Element li at 0x1d661115208>, <Element a at 0x1d661115248>]
"""
# 也可以指定匹配的节点名称:
result1 = html.xpath('//li')
# 通过 / 或 // 即可查找元素的子节点或子孙节点。
# 选择 li 节点的所有直接 a 子节点:
result2 = html.xpath('//li/a')
# 知道子节点,查询父节点可以用 .. 来实现:
result3 = html.xpath('//a[@href="link4.html"]/../@class')
# 获得 href 属性为 link4.html 的 a 节点的父节点的 class 属性
# parent::*也是父节点的表示(斜杠要加上)
テキストの取得
XPathのtext()メソッドを使用して、ノード内のテキストを取得します。
# 第一种
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)
# 第二种(子孙节点内部的所有文本)
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]//text()')
print(result)
# 第二种方法会获取到补全代码时换行产生的特殊字符
# 推荐使用第一种方法,可以保证获取的结果是整洁的。
物件取得
属性マッチングとは異なりますが、実際には@記号を使用することで実現できます。
# 获取节点的 href 属性
result = html.xpath('//li/a/@href')
print(result)
# 运行结果:['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
属性の複数値のマッチング
ノードの特定の属性に複数の値がある場合、以前の単一のマッチング方法は空です。
contains()メソッドを介して、最初のパラメーターが属性名で渡され、2番目のパラメーターが属性値で渡されます。属性に渡された属性値が含まれている限り、マッチングを完了することができます。
from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class, "li")]/a/text()')
print(result)
# 例中的li节点的class属性有2个值
# 运行结果:['first item']
複数の属性のマッチング
from lxml import etree
text = '''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
# 要用 and 操作符相连,相连之后置于 中括号 内进行条件筛选
# 这里的 and 其实是 XPath 中的运算符
順番に選択
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
# 获取第一个(注意,这里和代码中不同,序号是以1开头的,不是以0开头)
result = html.xpath('//li[1]/a/text()')
print(result)
# 获取最后一个
result = html.xpath('//li[last()]/a/text()')
print(result)
# 获取前两个
result = html.xpath('//li[position()<3]/a/text()')
print(result)
# 获取倒数第三个
result = html.xpath('//li[last()-2]/a/text()')
print(result)
"""
运行结果:
['first item']
['fifth item']
['first item', 'second item']
['third item']
"""
ここでは、last()やposition()などの関数が使用されます。XPathには、アクセス値、文字列、ロジック、ノード、シーケンスなどの処理関数を含む100を超える関数が用意されています。詳細については、使用ドキュメントを参照してください。
ノード軸の選択
XPathは、子要素、兄弟要素、親要素、祖先要素などの取得を含む、多くのノード軸選択方法を提供します。
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html"><span>first item</span></a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
# 获取所有祖先节点
result = html.xpath('//li[1]/ancestor::*')
print(result)
# 获取 div 祖先节点
result = html.xpath('//li[1]/ancestor::div')
print(result)
# 获取当前节点所有属性值
result = html.xpath('//li[1]/attribute::*')
print(result)
# 获取 href 属性值为 link1.html 的直接子节点
result = html.xpath('//li[1]/child::a[@href="link1.html"]')
print(result)
# 获取所有的的子孙节点中包含 span 节点但不包含 a 节点
result = html.xpath('//li[1]/descendant::span')
print(result)
# 获取当前所有节点之后的第二个节点
result = html.xpath('//li[1]/following::*[2]')
print(result)
# 获取当前节点之后的所有同级节点
result = html.xpath('//li[1]/following-sibling::*')
print(result)
"""
[<Element html at 0x231a8965388>, <Element body at 0x231a8965308>, <Element div at 0x231a89652c8>, <Element ul at 0x231a89653c8>]
[<Element div at 0x231a89652c8>]
['item-0']
[<Element a at 0x231a89653c8>]
[<Element span at 0x231a89652c8>]
[<Element a at 0x231a89653c8>]
[<Element li at 0x231a8965308>, <Element li at 0x231a8965408>, <Element li at 0x231a8965448>, <Element li at 0x231a8965488>]
"""
XPathと正規表現について
XPathは、主にXML形式のドキュメントの処理に使用されます。XMLドキュメントの階層構造に基づいて、指定されたノードへのパスを決定するため、明らかな階層構造を持つデータの処理に特に適しています。
正規表現は、任意の形式の文字列ドキュメントを処理できます。テキストの特性に基づいて、指定されたデータを照合して検索します。
個人的には、対象データが明確な階層構造を持っているのか、明らかな特徴を持っているのかを区別する必要があると思います3。この区別は明確で、何を選ぶかは基本的に決まっています。さらに、XPathは読みやすさと保守性に優れています。もちろん、一般的な正規表現の効率
は比較的高く、正規表現はそれほど複雑ではないことが前提です。