記事ディレクトリ
シリーズ記事インデックス
Python クローラーの基礎 (1): urllib ライブラリの使用方法の詳細な説明
Python クローラーの基礎 (2): xpath と jsonpath を使用してクロールされたデータを解析する
Python クローラーの基礎 (3): Selenium を使用して Web ページを動的に読み込む
Python クローラーの基礎 (4) :Pythonクローラーをもっと便利に使う リクエストライブラリの基礎
(5):Scrapyフレームワークの使い方
1. スクレイピーの紹介
1. スクレイピーとは何ですか?
Scrapy は、Web ページ データをクロールし、構造化データを抽出するために記述されたアプリケーション フレームワークであり、カプセル化されており、リクエスト (非同期スケジューリングと処理)、ダウンローダー (マルチスレッド ダウンローダー)、パーサー (セレクター)、ツイスト (非同期処理) などが含まれています。Web サイトのコンテンツのクロールの速度は非常に高速です。
2.スクレイピーインストール
# 进入到python安装目录的Scripts目录
d:
cd D:\python\Scripts
# 安装 可以使用国内源
pip install scrapy
3. 粗末なアーキテクチャ構成
(1) エンジン: 自動的に実行され、注意を払う必要はありません。すべてのリクエスト オブジェクトが自動的に整理され、ダウンローダーに配布されます。
(2) ダウンローダー:エンジンからリクエストオブジェクトを取得後、データをリクエストします。
(3) スパイダー: スパイダー クラスは、特定の (または特定の) Web サイトをクロールする方法を定義します。これには、クロール アクション (リンクをたどるかどうかなど) と、Web ページのコンテンツから構造化データを抽出する方法 (アイテムのクロール) が含まれます。言い換えれば、Spider はクローリング アクションを定義し、Web ページを分析する場所です。
(4) スケジューラ: 独自のスケジュール ルールがあるため、注意を払う必要はありません。
(5) パイプライン (アイテム パイプライン): データを最終的に処理するパイプラインは、データを処理するためのインターフェイスを予約します。アイテムがスパイダーに収集されると、アイテムはアイテム パイプラインに渡され、一部のコンポーネントが特定の順序でアイテムの処理を実行します。各アイテム パイプライン コンポーネントは、単純なメソッドを実装する Python クラスです。アイテムを受け取り、それを通じていくつかのアクションを実行します。また、アイテムがパイプラインを通過し続けるか、破棄されて処理されなくなるかどうかも決定します。
アイテム パイプラインの典型的なアプリケーションを次に示します:
(1) HTML データのクリーンアップ、(2) クロールされたデータの検証 (アイテムに特定のフィールドが含まれていることの確認)、(3) 重複のチェック (および破棄)、(4) 変換クロールされた結果をデータベースに保存します。
4. スクレイピーの仕組み
スパイダー -> スケジューラー (スケジューラー) -> スクレイピー エンジン (エンジン) -> ダウンローダー (ダウンローダー) -> インターネットからダウンロード ->
エンジンからスパイダーにデータをダウンロード -> エンジン xpath によるデータ分析 -> データ ストレージへのパイプラインを使用
2. スクレイピーの基本的な使い方
1. プロジェクトを作成する
プロジェクト ディレクトリに入り、cmd を開きます。
# 创建scrapy_test_001项目,项目名不能以数字、汉字开头
scrapy startproject scrapy_test
2. クローラーファイルを作成する
Spiders フォルダーにクローラー ファイルを作成するには
# cd 项目的名字\项目的名字\spiders
cd scrapy_test\scrapy_test\spiders
クローラー ファイルを作成します。http プロトコルを追加する必要がないことに注意してください。
# scrapy genspider 爬虫文件的名字 要爬取网页
scrapy genspider baidu www.baidu.com
このとき、spiders ディレクトリに baidu.py が生成されます。baidu.py
の内容を見てみましょう。
import scrapy
class BaiduSpider(scrapy.Spider):
# 爬虫的名字 用于运行爬虫的时候 使用的值
name = "baidu"
# 允许访问的域名
allowed_domains = ["www.baidu.com"]
# 起始的url地址 指的是第一次要访问的域名
start_urls = ["https://www.baidu.com"]
# 是执行了start_urls之后 执行的方法 方法中的response 就是返回的那个对象
# 相当于 response = urllib.request.urlopen()
# response = requests.get()
def parse(self, response):
pass
後で、parse メソッドで応答を処理できます。これが最終的なクロール結果になります。
3.(別紙)プロジェクト構成
4. クローラーコードを実行する
(1)baidu.pyを修正する
parse メソッドで、出力をカスタマイズします。
def parse(self,response):
print('输出正确!')
Spiders ディレクトリで次のコマンドを実行して、クローラー コードを実行します。
# scrapy crawl 爬虫的名字
scrapy crawl baidu
多くのコンテンツが出力されますが、印刷したものは何もありません。
(2) ロボットファイル
コンソールには、Baidu のロボット プロトコルが表示されます。
すべての Web サイトには、クロールが許可されていないものを定義するロボット紳士協定があります。Baidu のロボットを見てみましょう。
https://www.baidu.com/robots.txt
プロジェクトの settings.py ファイルのデフォルトは ROBOTSTXT_OBEY=True で、これはこの紳士協定に従うことを意味します。
この行をコメント アウトするだけです。
この時点で、クローラー コードを実行します。
# scrapy crawl 爬虫的名字
scrapy crawl baidu
この時点で、コマンドラインにカスタマイズした文が出力されます。
5. 応答の属性とメソッド
response.xpath(xpath_expression): XPath 式に基づいてデータを選択および抽出します。
response.css(css_expression): CSS セレクターに基づいてデータを選択および抽出します。
response.follow(url): 指定された URL に基づいて新しいリクエストを作成し、コールバック メソッドを通じて処理を続行します。
response.url: 現在の応答の URL を返します。
response.status: 現在の応答のステータス コードを返します。
response.headers: 現在の応答のヘッダー情報を返します。
response.body: 現在の応答の生のバイナリ コンテンツを返します。
response.text: 現在の応答のテキスト コンテンツを返します。
response.css('a::attr(href)').getall(): CSS セレクターを使用して、一致する要素の属性値をすべて抽出します。
response.xpath('//a/@href').extract(): XPath 式を使用して、一致するすべての要素の属性値を抽出します
6. 実戦:Baiduの[Baidu Click]ボタンのコンテンツを取得
def parse(self, response):
print('=====================')
input = response.xpath('//input[@id="su"]/@value')[0]
print(input.extract()) # 百度一下
print('=====================')
7. 実戦:オートホーム車の価格表を入手
import scrapy
class CarSpider(scrapy.Spider):
name = 'car'
allowed_domains = ['https://car.autohome.com.cn/price/brand-15.html']
start_urls = ['https://car.autohome.com.cn/price/brand-15.html']
def parse(self, response):
print('=======================')
name_list = response.xpath('//div[@class="main-title"]/a/text()')
price_list = response.xpath('//div[@class="main-lever"]//span/span/text()')
for i in range(len(name_list)):
name = name_list[i].extract()
price = price_list[i].extract()
print(name,price)
print('=======================')
3. スクレイピーシェルを使用する
1. スクレイピーシェルとは何ですか?
Scrapy ターミナルは、スパイダーを起動せずにクローリング コードを試してデバッグできる対話型ターミナルです。データを抽出するコードをテストするために使用することを目的としていますが、通常の Python ターミナルとして使用して、任意の Python コードをテストすることもできます。
このターミナルは、xPath または CSS 式をテストして、それらがどのように機能するかを確認し、クロールされた Web ページからデータを抽出するために使用されます。スパイダーを作成する場合、ターミナルは式コードを対話的にテストする機能を提供し、変更のたびにスパイダーを実行する手間を省きます。Scrapy ターミナルに慣れると、それがスパイダーの開発とデバッグに大きな役割を果たしていることがわかります。
2. ipython をインストールします (オプション)
# 进入到python安装目录的Scripts目录
d:
cd D:\python\Scripts
# 安装 可以使用国内源
pip install ipython
IPython がインストールされている場合、Scrapy ターミナルは (標準の Python ターミナルの代わりに) IPython を使用します。IPython ターミナルは他のターミナルよりも強力で、インテリジェントなオートコンプリート、強調表示された出力、その他の機能を提供します。
使用法: コマンド ラインに ipython を直接入力すると、新しいコマンド ウィンドウに独自の強調表示とオートコンプリートが表示されます。
3. スクレイピーシェルを使用する
# 直接在window的终端中输入scrapy shell 域名
# 直接在命令终端(不需要进入python或者ipython终端),执行完毕之后,自动进入ipython终端
scrapy shell www.baidu.com
ここで応答オブジェクトを直接取得して直接デバッグできます。
4. 実戦:Dangdang.comから製品データを取得
1. プロジェクトの初期化
# 在自定义目录中创建项目
scrapy startproject scrapy_dangdang
# 创建爬虫文件
# cd 项目的名字\项目的名字\spiders
cd scrapy_dangdang\scrapy_dangdang\spiders
# scrapy genspider 爬虫文件的名字 要爬取网页
# 中国古典小说网址:http://category.dangdang.com/cp01.03.32.00.00.00.html
scrapy genspider dang http://category.dangdang.com/cp01.03.32.00.00.00.html
# 运行
scrapy crawl dang
2. 項目ファイルの定義
プロジェクトによって自動的に生成された item.py で、クロールされるデータ形式を定義します。
import scrapy
class ScrapyDangdangItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 通俗的说就是你要下载的数据都有什么,固定写法:scrapy.Field()
# 图片
src = scrapy.Field()
# 名字
name = scrapy.Field()
# 价格
price = scrapy.Field()
3. 写真、名前、価格をクロールする
まず、これら 3 つの xpath を分析しましょう。
画像の xpath を取得します。//ul[@id="component_59"]/li/a/img/@src
画像は遅延ロードされるため、画像の属性を取得する必要がありますdata-original
。
タイトルの xpath を取得します。//ul[@id="component_59"]/li/p[@class="name"]/a/@title
価格の xpath を取得します。//ul[@id="component_59"]/li/p[@class="price"]/span[1]/text()
import scrapy
class DangSpider(scrapy.Spider):
name = "dang"
allowed_domains = ["category.dangdang.com"]
start_urls = ["http://category.dangdang.com/cp01.03.32.00.00.00.html"]
def parse(self, response):
# src = //ul[@id="component_59"]/li/a/img/@src
# name = //ul[@id="component_59"]/li/p[@class="name"]/a/@title
# price = //ul[@id="component_59"]/li/p[@class="price"]/span[1]/text()
# 所有的seletor的对象 都可以再次调用xpath方法
li_list = response.xpath('//ul[@id="component_59"]/li')
for li in li_list:
src = li.xpath('./a/img/@data-original').extract_first()
# 第一张图片和其他的图片的标签的属性是不一样的
# 第一张图片的src是可以使用的 其他的图片的地址是data-original
if src:
src = src
else:
src = li.xpath('./a/img/@src').extract_first()
name = li.xpath('./p[@class="name"]/a/@title').extract_first()
price = li.xpath('./p[@class="price"]/span[1]/text()').extract_first()
# 拿到所有信息
print(src + name + price)
4. パイプラインのパッケージ化
(1) 収量を知る
タイド関数は通常の関数ではなく、反復に使用できるジェネレーターです。
Yield は return と似たキーワードで、反復処理で yield に遭遇すると、yield の後の値 (右側) を返します。重要なのは、次の反復では、前の反復子が遭遇したyieldの後のコード(次の行)から実行が開始されるということです。
簡単な理解: yield は、return が値を返し、返された位置を記憶し、次の反復がこの位置 (次の行) から開始されることを意味します。
(2) item オブジェクトを構築してパイプラインに渡す
上記で画像、名前、価格を取得しました。引き続き parse メソッドで item オブジェクトを構築し、それをパイプラインに渡します。
from scrapy_dangdang.items import ScrapyDangdangItem
# 构造item对象
book = ScrapyDangdangItem(src=src,name=name,price=price)
# 获取一个book就将book交给pipelines
yield book
(3) settings.pyでパイプラインを有効にする
多くのパイプラインが存在する可能性があるため、パイプラインには優先順位が付けられます。優先順位の範囲は 1 ~ 1000 で、値が小さいほど優先順位が高くなります。
ITEM_PIPELINES = {
# 管道可以有很多个 那么管道是有优先级的 优先级的范围是1到1000 值越小优先级越高
"scrapy_dangdang.pipelines.ScrapyDangdangPipeline": 300,
}
(4) Pipeline.pyを編集する
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
# 如果想使用管道的话 那么就必须在settings中开启管道
class ScrapyDangdangPipeline:
# 在爬虫文件开始的之前就执行的一个方法 :open_spider
def open_spider(self,spider):
self.fp = open('book.json','w',encoding='utf-8')
# 执行过程:process_item
# item就是yield后面的book对象
def process_item(self, item, spider):
# 以下这种模式不推荐 因为每传递过来一个对象 那么就打开一次文件 对文件的操作过于频繁
# # (1) write方法必须要写一个字符串 而不能是其他的对象
# # (2) w模式 会每一个对象都打开一次文件 覆盖之前的内容
# with open('book.json','a',encoding='utf-8')as fp:
# fp.write(str(item))
self.fp.write(str(item))
self.fp.write('\n')
return item
# 在爬虫文件执行完之后 执行的方法 : close_spider
def close_spider(self,spider):
self.fp.close()
(5) 書き込んだjsonファイルを実行して表示する
scrapy crawl dang
5. 複数のパイプラインの使用
(1)pipelines.pyはパイプラインクラスを定義します
Pipelines.py では複数のクラスを定義でき、直接記述するだけです。
このクラスには、直接使用できるデフォルトのメソッドが 3 つあります。
import urllib.request
class ScrapyDangdangDownloadPipeline:
def process_item(self, item, spider):
url = 'http:' + item.get('src')
filename = './books/' + item.get('name') + '.jpg'
urllib.request.urlretrieve(url = url, filename= filename)
# 有返回值
return item
(2) settings.py がパイプラインを開きます
ITEM_PIPELINES = {
# 管道可以有很多个 那么管道是有优先级的 优先级的范围是1到1000 值越小优先级越高
"scrapy_dangdang.pipelines.ScrapyDangdangPipeline": 300,
'scrapy_dangdang.pipelines.ScrapyDangdangDownloadPipeline':301
}
(5) 書き込まれたjsonファイルと画像を実行して表示する
まず、Spider の下に Books ディレクトリを作成します。
# 执行
scrapy crawl dang
6. 複数ページのデータを取得する
import scrapy
from scrapy_dangdang.items import ScrapyDangdangItem
class DangSpider(scrapy.Spider):
name = "dang"
allowed_domains = ["category.dangdang.com"]
start_urls = ["http://category.dangdang.com/cp01.03.32.00.00.00.html"]
base_url = 'http://category.dangdang.com/cp'
page = 1
def parse(self, response):
# 。。。省略
# 每一页的爬取的业务逻辑全都是一样的,所以我们只需要将执行的那个页的请求再次调用parse方法
# 就可以了
# http://category.dangdang.com/pg2-cp01.03.32.00.00.00.html
# http://category.dangdang.com/pg3-cp01.03.32.00.00.00.html
# http://category.dangdang.com/pg4-cp01.03.32.00.00.00.html
if self.page < 100:
self.page = self.page + 1
url = self.base_url + str(self.page) + '-cp01.03.32.00.00.00.html'
# 怎么去调用parse方法
# scrapy.Request就是scrpay的get请求
# url就是请求地址
# callback是你要执行的那个函数 注意不需要加()
yield scrapy.Request(url=url,callback=self.parse)
5. 実戦:ムービーパラダイスの各ページからデータを取得
1.効果
最初のページのリストにある映画名である Movie Paradise を取得します。
次に、映画の詳細をクリックして、2 番目のページの詳細にある画像を取得します。
2. コアコード
mv.pyコアコード
import scrapy
from scrapy_movie_099.items import ScrapyMovie099Item
class MvSpider(scrapy.Spider):
name = 'mv'
allowed_domains = ['www.dygod.net']
start_urls = ['https://www.dygod.net/html/gndy/china/index.html']
def parse(self, response):
# 要第一个的名字 和 第二页的图片
a_list = response.xpath('//div[@class="co_content8"]//td[2]//a[2]')
for a in a_list:
# 获取第一页的name 和 要点击的链接
name = a.xpath('./text()').extract_first()
href = a.xpath('./@href').extract_first()
# 第二页的地址是
url = 'https://www.dygod.net' + href
# 对第二页的链接发起访问 并将name参数传入
yield scrapy.Request(url=url,callback=self.parse_second,meta={
'name':name})
def parse_second(self,response):
# 注意 如果拿不到数据的情况下 一定检查你的xpath语法是否正确
src = response.xpath('//div[@id="Zoom"]//img/@src').extract_first()
# 接受到请求的那个meta参数的值
name = response.meta['name']
movie = ScrapyMovie099Item(src=src,name=name)
yield movie
パイプライン.py:
from itemadapter import ItemAdapter
class ScrapyMovie099Pipeline:
def open_spider(self,spider):
self.fp = open('movie.json','w',encoding='utf-8')
def process_item(self, item, spider):
self.fp.write(str(item))
return item
def close_spider(self,spider):
self.fp.close()
settings.py でパイプラインを開きます。
ITEM_PIPELINES = {
'scrapy_movie_099.pipelines.ScrapyMovie099Pipeline': 300,
}
6. 実践的な戦闘: CrawlSpider を使用して Dudu.com からデータを取得します
1. CrawlSpider の概要
CrawlSpider は、scrapy.Spider を継承し、ルールを定義することができ、HTML コンテンツを解析するときに、リンク ルールに従って指定されたリンクを抽出し、そのリンクにリクエストを送信できます。
したがって、リンクを追跡する必要がある場合、つまり Web ページをクロールした後、リンクを抽出して再度クロールする必要がある場合、CrawlSpider の使用が非常に適しています。
リンクを抽出するための一般的な構文:
リンク エクストラクター。指定したリンクを抽出するルールを記述できます。
scrapy.linkextractors.LinkExtractor(
allow = (), # 正则表达式,提取符合正则的链接
deny = (), # (不用)正则表达式 不提取符合正则的链接
allow_domains = (), # (不用)允许的域名
deny_domains = (), # (不用)不允许的域名
restrict_xpaths = (), # xpath,提取符合xpath规则的链接
restrict_css = () # 提取符合选择器规则的链接
)
# 使用实例
# 正则用法:
links = LinkExtractor(allow = r'list_23_\d+\.html')
# xpath:
links = LinkExtractor(restrict_xpaths = r'//div[@class="x"]')
# css用法:
links = LinkExtractor(restrict_css='.x')
# 提取链接
links.extract_links(response)
2. プロジェクトを作成する
# 创建项目:scrapy startproject 项目的名字
scrapy startproject readbook
# 创建爬虫文件
# cd 项目名字\项目名字\spiders
cd readbook/readbook/Spiders
# scrapy genspider -t crawl 爬虫文件的名字 爬取的域名
scrapy genspider -t crawl read www.dushu.com/book/1188_1.html
read.py の内容が以前のものとは異なることがわかりました。
3. 項目を定義する
class ReadbookItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name = scrapy.Field()
src = scrapy.Field()
4. データの抽出
read.py:
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from readbook.items import ReadbookItem
class ReadSpider(CrawlSpider):
name = "read"
allowed_domains = ["www.dushu.com"]
# 注意,第一个url也要匹配规则!不然会跳过第一页
start_urls = ["https://www.dushu.com/book/1188_1.html"]
# 规则
rules = (Rule(LinkExtractor(allow=r"/book/1188_\d+.html"), callback="parse_item", follow=True),)
# 解析
def parse_item(self, response):
img_list = response.xpath('//div[@class="bookslist"]//img')
for img in img_list:
name = img.xpath('./@data-original').extract_first()
src = img.xpath('./@alt').extract_first()
book = ReadbookItem(name=name,src=src)
yield book
5. パイプラインを定義する
# settings.py
ITEM_PIPELINES = {
"readbook.pipelines.ReadbookPipeline": 300,
}
# pipelines.py
from itemadapter import ItemAdapter
class ReadbookPipeline:
def open_spider(self,spider):
self.fp = open('book.json','w',encoding='utf-8')
def process_item(self, item, spider):
self.fp.write(str(item))
return item
def close_spider(self,spider):
self.fp.close()
6.スタート
scrapy crawl read
実行後、book.json ファイルを表示します。
7.mysqlに保存
pymysqlをインストールします。
# 进入到python安装目录的Scripts目录
d:
cd D:\python\Scripts
# 安装 可以使用国内源
pip install pymysql
パイプラインを追加します。
# settings.py
ITEM_PIPELINES = {
"readbook.pipelines.ReadbookPipeline": 300,
# MysqlPipeline
'readbook.pipelines.MysqlPipeline':301
}
# 参数中一个端口号 一个是字符集 都要注意
DB_HOST = '192.168.1.1'
# 端口号是一个整数
DB_PORT = 3306
DB_USER = 'root'
DB_PASSWROD = '123'
DB_NAME = 'spider01'
# utf-8的杠不允许写
DB_CHARSET = 'utf8'
パイプライン.py:
# 加载settings文件
from scrapy.utils.project import get_project_settings
import pymysql
class MysqlPipeline:
def open_spider(self,spider):
settings = get_project_settings()
self.host = settings['DB_HOST']
self.port =settings['DB_PORT']
self.user =settings['DB_USER']
self.password =settings['DB_PASSWROD']
self.name =settings['DB_NAME']
self.charset =settings['DB_CHARSET']
self.connect()
def connect(self):
self.conn = pymysql.connect(
host=self.host,
port=self.port,
user=self.user,
password=self.password,
db=self.name,
charset=self.charset
)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
sql = 'insert into book(name,src) values("{}","{}")'.format(item['name'],item['src'])
# 执行sql语句
self.cursor.execute(sql)
# 提交
self.conn.commit()
return item
def close_spider(self,spider):
self.cursor.close()
self.conn.close()
7. 実戦:ポストリクエストの送信
キーコード:
import scrapy
import json
class TestpostSpider(scrapy.Spider):
name = 'testpost'
allowed_domains = ['https://fanyi.baidu.com/sug']
# post请求 如果没有参数 那么这个请求将没有任何意义
# 所以start_urls 也没有用了
# parse方法也没有用了
# start_urls = ['https://fanyi.baidu.com/sug/']
#
# def parse(self, response):
# pass
# start_requests是一个固定方法
def start_requests(self):
url = 'https://fanyi.baidu.com/sug'
data = {
'kw': 'final'
}
yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse_second)
def parse_second(self,response):
content = response.text
obj = json.loads(content,encoding='utf-8')
print(obj)