この記事のテキストと写真はインターネットからのものであり、学習とコミュニケーションのみを目的としており、商用目的ではありません。ご不明な点がございましたら、処理についてお問い合わせください。
次の記事は、Pythonクローラーとデータマイニング、著者のカップワイン氏からのものです。
I.はじめに
Webクローラー(WebスパイダーまたはWebロボットとも呼ばれます)は、特定のルールに従ってWorld WideWeb上の情報を自動的にクロールするプログラムまたはスクリプトです。他のあまり一般的に使用されない名前は、アリ、自動インデックス作成、シミュレーター、またはワームです。------百度百科事典
人間の言葉で言えば、クローラーは定期的に大量のデータを取得し、それらを処理して使用するために使用されます。これは、ビッグデータ、財務、機械学習などで必要なサポート条件の1つです。
現在、第1層の都市では、クローラーの給与と扱いは比較的客観的です。その後、中堅および上級のクローラーエンジニア、データアナリスト、ビッグデータ開発のポジションへの昇進はすべて良い移行です。
Pythonクローラー、データ分析、Webサイト開発、その他のケースチュートリアルビデオはオンラインで無料で視聴できます
https://space.bilibili.com/523606542
Python学習交換グループ:1039645993
2.プロジェクトの目標
今回紹介したプロジェクトは、それほど複雑である必要はありません。最終的な目標は、投稿の各コメントをデータベースにクロールし、データを更新して、クロールやクロール防止などの対策が繰り返されないようにすることです。
3.プロジェクトの準備
このパートでは、主に、この記事で使用されているツール、関連するライブラリ、Webページやその他の情報などを紹介します。
ソフトウェア:PyCharm
必要なライブラリ:Scrapy、selenium、pymongo、user_agent、datetime
ターゲットウェブサイト:
http://bbs.foodmate.net
プラグイン:chromedriver(バージョンが正しい必要があります)
4、プロジェクト分析
1.クロールWebサイトの構造を決定します
要するに:ウェブサイトの読み込み方法、レベルごとにデータレベルを取得するために投稿を正しく入力する方法、データを保存するために使用する形式などを決定します。
次に、Webサイトの階層構造、つまり、セクションに従って少しずつ投稿ページに入る方法を観察します。これは、このクローラータスクにとって非常に重要であり、コード作成の主要部分でもあります。
2.データをクロールする正しい方法を選択する方法は?
現在、私が知っているクローラーの方法は次のとおりです(不完全ですが、より一般的に使用されています)。
1)リクエストフレームワーク:このhttpライブラリは、必要なデータを柔軟かつシンプルにクロールするために使用できますが、プロセスは少し面倒であり、パケットキャプチャツールとともに使用してデータを取得できます。ただし、ヘッダーと対応するリクエストパラメータを決定する必要があります。そうしないと、データを取得できません。多くのアプリクロール、画像と動画のクロールと停止はいつでも可能で、比較的軽量で柔軟性があり、同時実行性と分散展開も非常に柔軟です。 、および機能はより良い実現である可能性があります。
2)Scrapyフレームワーク:Scrapyフレームワークは、クローラーにとって最も一般的に使用され、最高のクローラーフレームワークであると言えます。Scrapyは非同期であり、通常の代わりに読みやすいxpathを使用し、強力な統計とログシステムを使用します。同時に、異なるURLでのクロール、独立したデバッグに便利なシェルモードのサポート、統合フィルターの作成を容易にするミドルウェアの作成のサポート、パイプラインを介したデータベースへの保存などが可能です。これは、この記事で紹介するフレームワークでもあります(セレンライブラリと組み合わせて)。
5、プロジェクトの実現
1.最初のステップ:Webサイトのタイプを決定します
まず、それが何を意味し、どのWebサイトを見るかを説明します。まず、静的ロード、動的ロード(jsロード)、またはその他の方法であるかどうか、Webサイトのロード方法によって異なります。ロード方法が異なれば、必要な方法も異なります。それに対処する。次に、今日クロールされたWebサイトを観察したところ、これは時系列のフォーラムであることがわかりました。最初は、静的にロードされたWebサイトであると推測しました。次の図に示すように、プラグインを開いてjsのロードを整理しました。
更新した後、それは確かに静的なWebサイトであることがわかります(正常にロードできる場合は、基本的に静的にロードされます)。
2.ステップ2:階層を決定する
次に、今日クロールしたいWebサイトは、静的にロードされたWebサイトであるフードフォーラムWebサイトです。前回の分析ですでに理解しており、階層構造は次のとおりです。
おそらく上記のプロセスでは、下の図に示すように、合計3つのレベルのプログレッシブ訪問があり、その後、投稿ページに到達します。
コード表示の一部:
第1レベルのインターフェース:
def parse(self, response):
self.logger.info("已进入网页!")
self.logger.info("正在获取版块列表!")
column_path_list = response.css('#ct > div.mn > div:nth-child(2) > div')[:-1]
for column_path in column_path_list:
col_paths = column_path.css('div > table > tbody > tr > td > div > a').xpath('@href').extract()
for path in col_paths:
block_url = response.urljoin(path)
yield scrapy.Request(
url=block_url,
callback=self.get_next_path,
二次インターフェース:
def get_next_path(self, response):
self.logger.info("已进入版块!")
self.logger.info("正在获取文章列表!")
if response.url == 'http://www.foodmate.net/know/':
pass
else:
try:
nums = response.css('#fd_page_bottom > div > label > span::text').extract_first().split(' ')[-2]
except:
nums = 1
for num in range(1, int(nums) + 1):
tbody_list = response.css('#threadlisttableid > tbody')
for tbody in tbody_list:
if 'normalthread' in str(tbody):
item = LunTanItem()
item['article_url'] = response.urljoin(
tbody.css('* > tr > th > a.s.xst').xpath('@href').extract_first())
item['type'] = response.css(
'#ct > div > div.bm.bml.pbn > div.bm_h.cl > h1 > a::text').extract_first()
item['title'] = tbody.css('* > tr > th > a.s.xst::text').extract_first()
item['spider_type'] = "论坛"
item['source'] = "食品论坛"
if item['article_url'] != 'http://bbs.foodmate.net/':
yield scrapy.Request(
url=item['article_url'],
callback=self.get_data,
meta={'item': item, 'content_info': []}
)
try:
callback_url = response.css('#fd_page_bottom > div > a.nxt').xpath('@href').extract_first()
callback_url = response.urljoin(callback_url)
yield scrapy.Request(
url=callback_url,
callback=self.get_next_path,
)
except IndexError:
pass
3レベルのインターフェース:
def get_data(self, response):
self.logger.info("正在爬取论坛数据!")
item = response.meta['item']
content_list = []
divs = response.xpath('//*[@id="postlist"]/div')
user_name = response.css('div > div.pi > div:nth-child(1) > a::text').extract()
publish_time = response.css('div.authi > em::text').extract()
floor = divs.css('* strong> a> em::text').extract()
s_id = divs.xpath('@id').extract()
for i in range(len(divs) - 1):
content = ''
try:
strong = response.css('#postmessage_' + s_id[i].split('_')[-1] + '').xpath('string(.)').extract()
for s in strong:
content += s.split(';')[-1].lstrip('\r\n')
datas = dict(content=content, # 内容
reply_id=0, # 回复的楼层,默认0
user_name=user_name[i], # ⽤户名
publish_time=publish_time[i].split('于 ')[-1], # %Y-%m-%d %H:%M:%S'
id='#' + floor[i], # 楼层
)
content_list.append(datas)
except IndexError:
pass
item['content_info'] = response.meta['content_info']
item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
item['content_info'] += content_list
data_url = response.css('#ct > div.pgbtn > a').xpath('@href').extract_first()
if data_url != None:
data_url = response.urljoin(data_url)
yield scrapy.Request(
url=data_url,
callback=self.get_data,
meta={'item': item, 'content_info': item['content_info']}
)
else:
item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.logger.info("正在存储!")
print('储存成功')
yield item
3.ステップ3:クロール方法を決定する
静的なWebページであるため、最初にスクレイプフレームワークを使用してデータを直接取得することにしましたが、予備テストの結果、この方法は実際に実行可能であることがわかりました。しかし、当時、私は若くて軽薄で過小評価されていました。ウェブサイトの保護対策。忍耐力が限られているため、クロールを制限するタイマーが追加されていません。速度が原因でウェブサイトによって制限され、ウェブサイトが静的に読み込まれるウェブページから動的に読み込まれるウェブページ検証アルゴリズムに変更されてから、ウェブページ、直接アクセスはバックグラウンドによって拒否されます。
しかし、この種の問題はどうして私の賢さになるのでしょうか?少し考えた後(1日)、スキームをスクレイプフレームワーク+セレンライブラリの方法に変更し、chromedriverを呼び出してWebサイトへのアクセスをシミュレートしました。ロードされている場合、クロールされません。それだけです。フォローアップにより、この方法が実際に実行可能で効率的であることが証明されます。
コードの実装部分は次のとおりです。
def process_request(self, request, spider):
chrome_options = Options()
chrome_options.add_argument('--headless') # 使用无头谷歌浏览器模式
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--no-sandbox')
# 指定谷歌浏览器路径
self.driver = webdriver.Chrome(chrome_options=chrome_options,
executable_path='E:/pycharm/workspace/爬虫/scrapy/chromedriver')
if request.url != 'http://bbs.foodmate.net/':
self.driver.get(request.url)
html = self.driver.page_source
time.sleep(1)
self.driver.quit()
return scrapy.http.HtmlResponse(url=request.url, body=html.encode('utf-8'), encoding='utf-8',
request=request)
4.ステップ4:クロールされたデータのストレージ形式を決定します
言うまでもなく、この部分では、必要に応じて、items.pyでクロールする必要のあるデータ形式を設定します。この形式を使用してプロジェクトに保存するだけです。
class LunTanItem(scrapy.Item):
"""
论坛字段
"""
title = Field() # str: 字符类型 | 论坛标题
content_info = Field() # str: list类型 | 类型list: [LunTanContentInfoItem1, LunTanContentInfoItem2]
article_url = Field() # str: url | 文章链接
scrawl_time = Field() # str: 时间格式 参照如下格式 2019-08-01 10:20:00 | 数据爬取时间
source = Field() # str: 字符类型 | 论坛名称 eg: 未名BBS, 水木社区, 天涯论坛
type = Field() # str: 字符类型 | 板块类型 eg: '财经', '体育', '社会'
spider_type = Field() # str: forum | 只能写 'forum'
5.ステップ5:データベースの保存を確認します
このプロジェクト用に選択されたデータベースはmongodbです。これは非リレーショナルデータベースであるため、利点は明らかであり、フォーマット要件はそれほど高くなく、多次元データは柔軟に保存できます。これは一般的にクローラーに推奨されるデータベースです。 (私にredisを言わないでください、私がそれを知っているなら私はそれを使います、主にそうではありません)
コード:
import pymongo
class FMPipeline():
def __init__(self):
super(FMPipeline, self).__init__()
# client = pymongo.MongoClient('139.217.92.75')
client = pymongo.MongoClient('localhost')
db = client.scrapy_FM
self.collection = db.FM
def process_item(self, item, spider):
query = {
'article_url': item['article_url']
}
self.collection.update_one(query, {"$set": dict(item)}, upsert=True)
m
このとき、賢い友人の中には、同じデータが2回クロールされた場合はどうなるかという質問があります。(つまり、重複チェック機能です)
以前はこの質問について考えていませんでした。後で、大物に聞いたときにわかりました。これは、データを保存したときに行われました。次の文だけです。
query = {
'article_url': item['article_url']
}
self.collection.update_one(query, {"$set": dict(item)}, upsert=True)
投稿のリンクを介してクロールする重複データがあるかどうかを判断します。重複データが上書きされていると理解できる場合は、データを更新することもできます。
6.その他の設定
マルチスレッド、ヘッダー、パイプライン送信シーケンスなどの問題はすべて、settings.pyファイルに設定されています。詳細については、エディターのプロジェクトを参照してください。ここでは詳しく説明しません。
セブン、エフェクトディスプレイ
1. [実行]をクリックすると、次の図に示すように、結果がコンソールに表示されます。
2.途中で、キューに多くの投稿を積み上げるクロールタスクがあり、マルチスレッド処理が行われます。16スレッドを設定しましたが、速度は依然として非常に印象的です。
3.データベースデータの表示:
content_infoには、各投稿のすべてのコメントと関連ユーザーの公開情報が格納されます。