pythonは再帰的に汎用爬虫類解析器を実現する。


私たちは爬虫類を書く過程で、アンチウオークを研究する以外に、ほとんどの時間は解析ロジックを書いています。では、命が短いのに、なぜ私たちは汎用解析器を書かないのですか?そうですよどうしてですか?きちんと開け
需要分析
爬虫類解析のためのウェブページのタイプは、他にありません)、json、およびいくつかのバイナリファイル(video、excelファイルなど)です。汎用解析器を作るには、ウェブページの内容を統一した形式に変換し、対応する解析規則で解析します。例えば、すべてのページの内容をhtml形式に変換して、xpathで抽出します。

もう一つは設定ファイルがあらかじめ告知されている方式で、どのようなタイプに設定されているかを解析します。
ウェブページの形式を統一するには、大量のウェブページのコンテンツ形式の変換が必要であり、設定ファイルは事前に告知すると、設定時により多くの解析フィールドを指定する必要があります。比較すると、将来の変更が多いのは配置規則であり、コアコードを必要とせず、バグを導入する可能性は低い。そこで、ここでは第二の方法で解像度を実現します。
さらに分析する
解析器はウェブページの内容の抽出に対して、本質的には地元のコンピュータでファイルを探して整理しています。たとえば次のように

解析内容はその中から私たちが欲しい情報を抽出し、希望のフォーマットに整理することです。例えば上記の内容は、私達が抽出した形式はこうです。

{
  "design": "   .psd",
  "software": "sketch.dmg"
}
実際の爬虫類開発の過程では、ウェブページの形式は上記よりずっと複雑です。実は一番多くの問題に遭遇したのはリストのセットにリストが埋め込まれています。このような形式を抽出する必要があります。例えば次のような形です。

{
    "a": "a",
    "b": [
        {"c": "c1", "d": "d1"},
        {"c": "c2", "d": "d2"}]
}
彼が情報を抽出したら、こうなるべきです。

[
  {
    "a": "a",
    "c": "c1",
    "d": "d1"
  },
  {
    "a": "a",
    "c": "c2",
    "d": "d2"
  }
]
小さなパートナーがアルゴリズムに慣れていれば、このような遍歴は再帰的に書くのに非常に便利であることがわかるはずです。ただし、pythonは再帰層数を限定しています。リトルパートナーは、再帰的に限定された層数を次のように調べられます。

import sys
print(sys.getrecursionlimit())

>>>1000
こちらの限られた階数は1 kです。ウェブページを解析するのには十分です。もしどの人がページ解析ロジックを1000層にはまったら、直接に社長にこのページを放棄するように提案します。
さらに分析する
共通解析については,構成解析規則によりページの対応情報を抽出することが分かった。リスト階層のあるウェブページについては、再帰的な巡回問題にも関連しているかもしれない。この解析規則はどう配置しますか?実は簡単です。各階層に入る前に、この層のデータ形式を指定してください。

{
  "a": "a",
  "b": [
          {"c": "c1", "d": "d1"},
          {"c": "c2", "d" : "d2"}
       ]
}
ネスト情報を抽出したいです。私たちの解析規則はこのようにすべきです。

[
 {
  "$name": "a",
  "$value_type": "raw",
  "$parse_method": "json",
  "$parse_rule": "a",
  "$each": []
 },
 {
  "$name": "__datas__",
  "$value_type": "recursion",
  "$parse_method": "json",
  "$parse_rule": "b",
  "$each": [
        {  
         "$name": "c",
          "$value_type": "raw",
         "$parse_method": "json",
         "$parse_rule": "c",
         "$each": []
        },
        {  
         "$name": "d",
          "$value_type": "raw",
         "$parse_method": "json",
         "$parse_rule": "d",
         "$each": []
        }
      ]
 }
]
$nameフィールドは、最終的には、最外層のデータが所有するフィールド名を望んでいます。もちろん、内部層に再帰する必要があるフィールドであれば、リストを__u_u u u u u_に保存します。datas_,そしてこれによってダタス内部構造の解析を行う。最終的に私達が得たデータ構造はこうなるべきです。

[
  {"a": "a", "c": "c1", "d": "d1"}, 
  {"a": "a", "c": "c2", "d": "d2"}
]
以上はjsonの解析規則だけを示しましたが、もし解析対象を持ってきたら?簡単です。解析方式をxpathオブジェクトに変えて、xpath解析文法に入ればいいです。
コードの実装
全部で二つの部分に分けて、一部はもとの最終結果と規則によって包装して、全部のrecursionロジックに関連するフィールドを転換します。コードは次の通りです。

def _pack_json(result, rules):
        item = {}

        for p_rule in rules:

            if p_rule.get("$value_type") == "raw":
                if p_rule.get("$parse_method") == "json":
                    item[p_rule.get("$name")] = glom(result, p_rule.get("$parse_rule"))

            elif p_rule.get("$value_type") == "recursion":
                if p_rule.get("$parse_method") == "json":
                    tmp_result = glom(result, p_rule.get("$parse_rule"))
                    total_result = []
                    for per_r in tmp_result:
                        total_result.append(_pack_json(per_r, p_rule.get("$each")))
                    item[p_rule.get("$name")] = total_result
        return item
もう一つの部分は前のステップで得られたものを解析して、包装した結果を解凍します。埋め込まれたすべてのデータを最外層に引き上げる予定です。コードは下記の通りです。

def _unpack_datas(result: dict) -> list:
        if "__datas__" not in result:
            return [result]

        item_results = []
        all_item = result.pop("__datas__")

        for per_item in all_item:
            if "__datas__" in per_item:
                tmp_datas = per_item.pop("__datas__")
                for per_tmp_data in tmp_datas:
                    tmp_item = _unpack_datas(per_tmp_data)
                    for per_tmp_item in tmp_item:
                        item_results.append({**per_tmp_item, **per_item})
            else:
                item_results.append({**result, **per_item})

        return item_results
後にもう1階を包んで入り口を実行すればいいです。完全なコードは以下の通りです。

from loguru import logger

from glom import glom


def parse(result, rules):

    def _pack_json(result, rules):
        item = {}

        for p_rule in rules:

            if p_rule.get("$value_type") == "raw":
                if p_rule.get("$parse_method") == "json":
                    item[p_rule.get("$name")] = glom(result, p_rule.get("$parse_rule"))

            elif p_rule.get("$value_type") == "recursion":
                if p_rule.get("$parse_method") == "json":
                    tmp_result = glom(result, p_rule.get("$parse_rule"))
                    total_result = []
                    for per_r in tmp_result:
                        total_result.append(_pack_json(per_r, p_rule.get("$each")))
                    item[p_rule.get("$name")] = total_result
        return item

    def _unpack_datas(result: dict) -> list:
        if "__datas__" not in result:
            return [result]

        item_results = []
        all_item = result.pop("__datas__")

        for per_item in all_item:
            if "__datas__" in per_item:
                tmp_datas = per_item.pop("__datas__")
                for per_tmp_data in tmp_datas:
                    tmp_item = _unpack_datas(per_tmp_data)
                    for per_tmp_item in tmp_item:
                        item_results.append({**per_tmp_item, **per_item})
            else:
                item_results.append({**result, **per_item})

        return item_results

    pack_result = _pack_json(result, rules)
    logger.info(pack_result)
    return _unpack_datas(pack_result)
以上、汎用解析器の完全な例です。ケースではJsonに対するサポートのみを実現し、パートナーは自分のプロジェクトに基づいて他の解析形式に改造することができます。汎用解析は実は鶏が怠けるために書いたのです。鶏が発見したので、爬虫類の開発では、ほとんどの仕事はこの部分を解析するのに時間がかかります。共通解析の先端ページがあり、運営やデータアナリストは自分のニーズに応じて自分が登りたいサイトを配置することができます。人生は短くて、分かります。魚を触りに行きました
実現方法は、githubを参照してください:https://github.com/hacksman/learn_lab/blob/master/small_bug_lab/general_パーサー.py
以上はpythonを再帰的に汎用爬虫類解析器の詳細を実現しました。python再帰的に爬虫類解析器を実現するための資料については他の関連記事に注目してください。