Web クローラー開発のための実用的なソース コード: https://github.com/MakerChen66/Python3Spider
オリジナルであることは簡単ではありません。この記事では盗作と転載が禁止されています。長年の実用的なクローラー開発経験の要約であり、侵害を調査する必要があります。 !
目次
1. XPthの導入
正規表現を使ってページ情報を抽出すると面倒で面倒に感じますし、場所を間違えるとマッチングに失敗する可能性もあるので、正規表現を使ってページ情報を抽出するのはまだ少し不便です。ページ
を解析するとき、XPth または CSS セレクターを使用して特定のノードを抽出し、対応するメソッドを呼び出してそのテキストまたは属性を取得できます。必要な情報を抽出することはできませんか?
Python にはこのような解析ライブラリが多数あり、その中には lxml、Beautiful Soup、pyquery などのより強力なライブラリがあります。このうち Beautiful Soup については以前紹介しましたが、今回は XPth セレクターを使用して lxml 解析ライブラリ内のノード要素を検索する方法を紹介します。効率も向上します。大幅に向上します
2.XPthの利用
XPth、正式名はXML Path Language、つまりXMLパス言語です。XML文書内の情報を検索するための言語ですが、HTML文書の検索にも適しています
2.1 XPthの概要
XPath の選択機能は非常に強力で、文字列、値、時間マッチング、ノードおよびシーケンス処理などの 100 を超える組み込み関数を提供します。探したいほぼすべてのノードは XPath で選択できます XPath
の公式 Web サイト:
https://www.w3.org/TR/xpath/
2.2 XPathの共通ルール
表現 | 説明 |
---|---|
ノード名 | このノードのすべての子ノードを選択します |
/ | 現在のノードから直接の子ノードをすべて選択します |
// | 現在のノードからすべての子孫ノードを選択します |
。 | 現在のノードを選択 |
… | 現在のノードの親ポイントと子ポイントを選択します |
@ | 属性を選択 |
一般的な XPath ルールはここにリストされています。例は次のとおりです。
//div[@name='loginname']
これは XPath ルールであり、名前が div で属性名が loginname であるすべてのノードを選択することを意味します
。ライブラリ lxml、XPath を使用して HTML を見つけて解析する
2.3 インストール
良い仕事をしたい場合は、まずツールを研ぎ澄ます必要があります。まず、解析ライブラリ lxml をインストールします。すでにインストールされている場合は無視してください。
pip install lxml -i https://pypi.doubanio.com/simple
ここで、-i はインストール URL を指定するために使用されますが、この URL は Douban ソースですが、清華ソース、Ali ソースなどを自分で使用することもできます。インストールに国内ミラーソースを使用する理由は、デフォルトのインストールURLが外部ネットワークとなっており、接続タイムアウトによりインストールが失敗する可能性があるためであり、ご理解は深いと思います。国内のミラーソースを使用し、
PyCharmを使用してソフトウェア内にインストールすることもできます
3. XP 番目の例
3.1 インスタンスの紹介
次に、次の例を通じて、XPath を使用して HTML を解析するプロセスを感じてみましょう。
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)
result = etree.tostring(html)
print(result.decode('utf-8'))
まず lxml ライブラリの etree モジュールをインポートし、次に HTML テキストを宣言し、HTML クラスを呼び出して初期化することで、XPath 解析オブジェクトが正常に作成されます。上記の HTML テキストの最後の li ノードは閉じられていないことに注意してください。ただし、etree は HTML テキストを自動的に修正できます。ここでは、tostring(
) メソッドを呼び出して修正された HTML コードを出力しますが、結果はバイト型であり、 decode() メソッドで str 型に変換し、出力結果は以下のようになります:
処理後、li ノードのラベルが完成し、body ノードと html ノードが自動的に追加されたことがわかります
。テキスト ファイルを直接読み込んで分析することもできます。例は次のとおりです。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
test.html の内容は上記の例の HTML コードで、./ は現在のディレクトリを示し、出力は次のようになります。
3.2 すべてのノード
通常、// で始まる XPath ルールを使用して、要件を満たすすべてのノードを選択します。前の HTML を例に挙げると、すべてのノードを選択したい場合は、次のようにすることができます。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
出力結果は次のとおりです。
* を使用してすべてのノードに一致します。つまり、HTML テキスト全体のすべてのノードが取得されます。返されるのはリストで、各要素のタイプは Element で、その後に html、body、div、ul、li、a などのノード名が続きます。もちろん、すべてのノードがリストに含まれます。ここでのマッチングではノード名を指定することもできます
。たとえば、すべての li ノードを取得したい場合は、次のようにします。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li')
print(result)
print(result[0])
すべての li ノードを選択するには、 // とノード名を使用し、呼び出し時に xpath() メソッドを直接使用します。
出力結果もリスト形式であり、各要素が Element オブジェクトであることがわかります。 。オブジェクトの 1 つを取り出したい場合は、角かっこを直接使用して、[0] などのインデックスを追加できます。
3.3 子ノード
/ または // を使用して、要素の子ノードまたは子孫ノードを見つけることができます。li ノードの直接の子ノードをすべて選択したい場合は、次のようにすることができます。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a')
print(result)
ここで、最後
に /a を付けると、すべての li ノードの直接の子ノード a がすべて選択されることになりますので、li ノードの子孫ノード a をすべて選択したい場合は、//a と記述します。もちろん、このように書いた出力結果は上記と同じですが、li ノードの後に a ノードが 1 つしかないので同じです。
3.4 親ノード
すべての直接の子ノードまたは子孫ノードは / または / / で選択できることはわかっています。では、子ノードがわかっている場合、親ノードを見つけるにはどうすればよいでしょうか? これは次のように実現できます。
href 属性値が link4.html であるノードを選択し、その親ノードの class 属性値を取得する場合、実装コードは次のようになります。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)
出力は次のとおりです。
['item-1']
[Finished in 0.5s]
test.html を確認すると、「item-1」が取得したターゲット li ノードのクラスとまったく同じであることがわかります。
また、parent:: を通じて親ノードを取得することもできます。コードは次のとおりです。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/parent::*/@class')
print(result)
出力も上記と同じです。この書き方をノード軸と呼びます。詳しくは第 4 回を参照してください。
3.5 属性のマッチング
選択する場合、@ 記号を使用して属性をフィルタリングできます。たとえば、クラスが item-0 である li ノードを選択したい場合は、次のように実装できます。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]')
print(result)
出力は次のとおりです。
[<Element li at 0x2176e366a48>, <Element li at 0x2176e366a88>]
一致結果は2つありますが、正しいかどうかは後ほど検証します
3.6 テキストの取得
XPath の text() メソッドは、ノード内のテキストを取得してから、前の li ノード内の a ノードのテキストを取得しようとします。コードは次のとおりです。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)
出力は次のとおりです。
['first item', 'fifth item']
ここでは戻り値が 2 つあることがわかります。まず属性が item-0 であるすべての li ノードを選択し、次にその直接の子ノード a をすべて使用/選択し、そのテキストを取得しました。まさに私たちが望んでいた 2 つの結果でした
3.7 属性の取得
text() を使用してノードのテキストを取得します。では、ノードのプロパティを取得するにはどうすればよいでしょうか? @ 記号を使用してください。たとえば、すべての li ノードの下にあるすべての a ノードの href 属性を取得したい場合、コードは次のとおりです。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a/@href')
print(result)
出力は次のとおりです。
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
ここでは、@href を通じてノードの href 属性を取得できます。
ここでの方法は属性マッチングとは異なることに注意してください。属性マッチングとは、[@href="link2.html"] のように、角括弧と属性名と値で特定の属性を制限することです。ここでの @href は特定の属性を指します。両者は区別する必要がある
3.8 属性の複数値のマッチング
場合によっては、ノードのプロパティに複数の値が含まれることがあります。次に例を示します。
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[@class="li"]/a/text()')
print(result)
出力は次のとおりです。
[]
空のリストは、テキスト text 内の li ノードの class 属性に li と li-first の 2 つの値があり、このとき、以前の属性マッチングを使用して取得するため、マッチングできません。このとき、contains() 関数を
使用する必要があり、コードは次のように書き換えることができます。
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)
出力は次のとおりです。
['first item']
このメソッドは、ノードのクラス属性が通常複数の値を持つなど、ノードの属性に複数の値がある場合によく使用されます。
3.9 複数属性のマッチング
さらに、複数の属性に基づいてノードが決定され、複数の属性を同時に照合する必要がある状況に遭遇することもあります。この時点で、演算子を使用して接続できます。例は次のとおりです。
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()')
print(result)
出力は次のとおりです。
['first item']
ここの li ノードは別の属性名を追加します。このノードを決定するには、class 属性と name 属性に従って同時に選択する必要があります。1 つの条件は、class 属性に 1i 文字列が含まれていること、もう 1 つの条件は、name 属性が項目文字列であることです。この 2 つは必要です。同時に満たされる必要があり、and 演算子で接続する必要があります。接続後、条件付きフィルタリングのために角かっこで囲みます。
ここでの and は、実際には XPath の演算子です。さらに、or、mod などの多くの演算子があります。その他の演算子については、リンクを参照してください:
https://www.w3school.com.cn/xpath/xpath_operators.asp
3.10 順次選択
場合によっては、選択時に一部の属性が複数のノードに同時に一致することがありますが、2 番目のノードや最後のノードなど、そのうちの 1 つのノードだけが必要な場合、このときどうすればよいでしょうか? 現時点では、次のように使用できます
。角かっこ内のインデックスを渡す方法では、特定の順序でノードが取得されます。例は次のとおりです。
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)
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)
初めて選択するときは、最初の li ノードを選択し、角括弧内に数値 1 を渡すだけです。これはコードとは異なります。シリアル番号は 0 ではなく 1 から始まります。
2 番目の選択では、最後の li ノードを選択しました。角括弧内で last() を渡すだけで、最後の li ノードが返されます。
3 番目の選択では、位置が 3 未満の li ノード、つまり位置番号が 1 と 2 のノードを選択しました。結果は最初の 2 つの li ノードです。
4 番目の選択では、最後から 2 番目の li ノードを選択し、角かっこ内の last0)-2 を渡しました。last() が最後なので、 last()-2 は最後から 3 番目になります。
出力は次のとおりです。
ここでは、last() や Position() などの関数を使用します。XPah では、アクセス、値、文字列、ロジック、ノード、シーケンス、その他の処理関数を含む 100 以上の関数が提供されています。その他の関数と機能については、リンク http://www.w3school.com.cn を参照してください
。 /xpath/xpath_functions.asp
4.XP番目のノード軸
4.1 XPth共通ノードの抽出
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)
result = html.xpath('//li[1]/ancestor::div')
print(result)
result = html.xpath('//li[1]/attribute::*')
print(result)
result = html.xpath('//li[1]/child::a[@href="link1.html"]')
print(result)
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)
出力は次のとおりです。
初めて選択するときは、祖先軸を呼び出してすべての祖先ノードを取得します。その後にコロンを 2 つ続ける必要があります。次に、ノードのセレクターがあります。ここでは * を直接使用します。これは、すべてのノードと一致することを意味します。そのため、返される結果は、html、body、div、ul を含む、最初の li ノードのすべての祖先ノードになります。
2 番目の選択では、制限を追加しました。今回はコロンの後に div を追加し、結果が div の祖先ノードのみになるようにしました。
3 番目の選択では、すべての属性値を取得するために属性軸を呼び出し、その後にノードのすべての属性を取得することを意味するセレクターまたは * が続きます。戻り値は li ノードのすべての属性値です
4 番目の選択では、子軸を呼び出して、すべての直接の子ノードを取得します。ここでは制限を追加しています。href 属性が である link1.html のノードを選択します。
5 番目の選択では、子孫軸を呼び出してすべての子孫ノードを取得します。ここでは、span ノードを取得する条件を追加します。そのため、返される結果には、span ノードのみが含まれ、a ノードは含まれません。
6 番目の選択では、次の軸を呼び出して、現在のノードの後のすべてのセクションを取得します。ここでは * マッチングを使用していますが、インデックス選択を追加しているため、2 番目に続くノードのみが取得されます。
7 番目の選択では、次の兄弟軸を呼び出して、現在のノードの後のすべての兄弟ノードを取得します。ここでは * マッチングを使用しているため、後続のすべての兄弟ノードがフェッチされます。
4.2 概要
基本的に、使用できる XPath セレクターを紹介しました。XPath セレクターは非常に強力で、多くの機能が組み込まれており、習熟すると、HTML 情報の抽出効率が大幅に向上します。
ノード軸の使用法について詳しくは、リンク
https://www.w3school.com.cn/xpath/xpath_axes.aspを参照してください。
XPath の詳しい使用方法については、リンク
https://www.w3school.com.cn/xpath/index.aspを参照してください。
lxml ライブラリの詳しい使用方法については、公式 Web サイトを確認してください:
https://lxml.de/
五、原文を読む
私のオリジナルの公開アカウントの原文へのリンク:原文を読む
オリジナリティを高めるのは簡単ではありません。役に立ったと思ったら、高評価をいただければ幸いです。ありがとうございます。
6. 著者情報
著者: Xiaohong の釣り日報、目標: プログラミングをもっと面白くする!
元の WeChat 公開アカウント:「Xiaohong Xingkong Technology」。アルゴリズム、クローラー、ウェブサイト、ゲーム開発、データ分析、自然言語処理、AI などに重点を置いています。ご注目をお待ちしております。一緒に成長し、コーディングしましょう!
転載指示: この記事は盗作と転載を禁止しており、侵害があれば調査する必要があります。