PythonでのcURL使用ガイド

この記事では、PythonとcURLを組み合わせて、GET、POST、PUTリクエストを自動化したり、ウェブページをダウンロードしたりする方法を説明します。
4 min read
Guide to using cURL with Python

cURLは、ネットワーク上でデータを転送するための多用途なオープンソースコマンドラインツールです。豊富なパラメータを使用できるため、ほとんどのリクエストに対応できます。また、cURLは拡張性があり、基本的に最新のプログラミング言語すべてのインターフェースを備えています。

cURLをプログラミング言語で使用すると、多くの利点が得られます。例えば、デバッグやウェブスクレイピングのユースケースとして、リクエストの作成を自動化できます。

この記事では、PythonとcURLを組み合わせて、GET、POST、PUTリクエストを自動化したり、ウェブページをダウンロードしたりする方法を説明します。

cURLとは?  

cURLはソフトウェアプロジェクトですが、その名前はlibcurlとして知られるライブラリとcurlとして知られるコマンドラインツール(libcurlを使用)の2つの製品でも使用されています。この記事では、curlはコマンドラインツールを指します。

curlは多機能と言われていますが、その核となるタスクはシンプルで、さまざまなネットワークプロトコルでデータを転送することです。今日のウェブの複雑さを考慮し、curlには最も複雑なリクエストを処理するための膨大なオプションのリストが付属しています。

curlは1996年にHttpGetとして初めてリリースされ、その後urlgetという名前を経てcurlとなりました。最初のユースケースは、為替レートを取得してIRCチャンネルで使用することでした。現在、curlはFTP(S) HTTP(S) (POST、GET、PUT)、IMAPPOP3MQTTSMBなど、さまざまな方法を介したデータ転送をサポートしています。さらに、curlはCookieやSSL証明書を処理できます。

curlは、HTTPS経由で接続を確立すると、リモートサーバー証明書を取得してCA証明書ストアと照合し、リモートサーバーがその主張するものであることを確認します。例えば、以下のリクエストは、Bright DataウェブサイトにHTTPSリクエストを送信し、値helloを持つgreetingとして知られているCookieを設定します。

curl --cookie "greeting=hello" https://www.brightdata.com

Pythonでcurlを使用する理由

curlは多用途のツールですが、Pythonでそれを使いたい主な理由が1つあります。Pythonはリクエストを自動化できます。以下に、この組み合わせが有効な3つのユースケースを示します。  

ウェブスクレイピング

ウェブスクレイピングとは、1つまたは複数のウェブページから(しばしば大量の)データを収集する手法です。Pythonでデータをスクレイピングする場合、しばしばrequestsライブラリが利用されます。再帰的にスクレイピングする場合は、wget を使用できます。しかし、複雑なHTTP(S)呼び出しを伴う高度なスクレイピングのユースケースには、Pythonとcurlの組み合わせが理想的です。

ウェブページのデータは、HTTP(S)リクエストを生成して処理する1つのcurlコマンドで収集できますが、それを再帰的に実行することはできません。Pythonのコードにcurlを組み込むことで、リクエストパラメータ、Cookie、ユーザーエージェントなどの要素を操作して、ウェブサイト上のナビゲーションパスをシミュレートできます。

ナビゲーションを修正する必要さえありません。スクレイピングされたコンテンツを条件とすることで、それぞれの新しいリクエストは完全に動的になります。

例えば、人気のあるニュースサイトのコメントセクションをスクレイピングしていて、作成者のプロフィールページのみをスクレイピングしたい場合、コメントに憎悪関連のキーワードが含まれているなら、スクレイピングしたコメントに応じた条件ステートメントを作成し、この動的フィルタを簡単に適用できます。

さらに、多くのウェブサイトには、大量のページのスクレイピングを困難にする安全メカニズムがあります。分散型サービス妨害(DDoS)対策やreCAPTCHAプロンプトのことを考えてみてください。一定のルールを適用し、リクエストの間に一時停止を置くことで、人間の行動をシミュレートして検知されにくくできます。

テストとデバッグ

自分のウェブサイトでcurlを使うのは馬鹿げているように思えますが、テストやデバッグのコンテキストでは便利です。アプリケーションの機能を1つ以上テストしたりデバッグしたりするのは、しばしば面倒な作業です。さまざまな設定やパラメータを使って、繰り返しテストする必要があります。市販のテストツールはたくさんありますが、Pythonとcurlを使えば、簡単にテストをセットアップできます。

例えば、Cookieを使用し、リファラーに依存し、ブラウザ(つまりユーザーエージェント)ごとに細かい違いがあり、チェックアウトフローのすべてのステップをPOSTリクエストの本体にパックする、(複雑な)オンラインサービス用の新しいチェックアウトフローをリリースする場合、手動ですべてのバリエーションをテストすると時間がかかる可能性があります。Pythonでは、パラメータセット全体を含む辞書を作成し、可能な組み合わせごとにcurlを使ってリクエストを送信できます。

ワークフローの自動化

テスト、デバッグ、ウェブスクレイピングに加えて、curlはワークフロー自動化のユースケースでも使用できます。例えば、多くのデータ統合パイプラインは、CSVや Apache Parquetファイルなどのデータエクスポートのダンプを繰り返し行うことから始まります。(S)FTP サーバーの新しいファイルをポーリングするPythonアプリケーションを使用すると、データダンプのコピーを完全に自動化できます。

または、メールフックの設定を検討してください。アプリケーションがクエリを含む電子メールメッセージをポーリングできたら、どれだけの日常業務が自動化されるか想像してみてください。POP3またはIMAPプロトコル経由で新しいメッセージをポーリングすることにより、メールボックスが特定のメールを受信したときにPythonアプリケーションをトリガーできます。

PythonでcURLを使う方法

Pythonでcurlを使ったリクエストを行うには、さまざまな方法があります。この記事では、2つの選択肢を取り上げます。1つ目は、ossubprocess Pythonパッケージを使って、コマンドラインでcurlリクエストをシミュレートする方法です。この直接的なアプローチでは、プログラム的にオペレーティングシステムのコマンドラインインターフェイスにコマンドを送信します。  

2つ目は、PycURLパッケージを使用する方法です。Pythonを使ってウェブサイトをスクレイピングする他の方法(curlを使用しない)について学びたい場合は、このBright Data Pythonによるスクレイピングガイドをご覧ください。

前提条件

このチュートリアルを始める前に、curlのダウンロードとインストールが完了していることを確認してください。Windowsをお使いの方は、PATH環境変数にcurlを追加してcurlコマンドを簡単に実行できるようにしておいてください。

Pythonとオペレーティングシステムとのインターフェースを作成するには、さまざまなパッケージを利用できます。ただし、代表的なものはossubprocessの2つです。両者をインストールするには、以下のpipコマンドを実行します。

pip install os subprocess

curlとosを使ってリクエストを作成する

osパッケージは、極めてシンプルなパッケージです。応答を処理せずにcurlリクエストを実行する場合、必要なコードは2行だけです。前の例で説明したCookieを渡すだけで、出力がoutput.txtファイルに書き込まれます。

import os
os.system('curl -o output.txt --cookie "greeting=hello" -k https://curl.se')

応答をファイルに書き込むのではなく、Pythonで処理する場合は、次のセクションで説明するsubprocessパッケージを使用する必要があります。

次のコードは、同じステートメントを実行しますが、応答をファイルに書き込む代わりに、stdoutstderrをタプルとして出力します。この出力は、Beautiful Soupなどの他のPythonパッケージで処理できます。

import shlex
import subprocess
shell_cmd = shlex.split('curl --cookie "greeting=hello" -k https://curl.se')
process = subprocess.Popen(shell_cmd,
                    stdout = subprocess.PIPE,
                    stderr = subprocess.PIPE,
                    text = True,
                    shell = True
                    )
std_out, std_err = process.communicate()
std_out.strip(), std_err

PycURLを使用する

Pythonでターミナルとやり取りする代わりに、PycURLパッケージを使用できます。Linuxユーザーであれば、幸運なことにpipを使ってPycURLをインストールできます。

pip install pycurl
pip install certifi

HTTPSプロトコル経由でやり取りするためのcertifiもインストールしてください。問題が発生した場合は Stack Overflowのこちらの指示に従ってください。

PycURLはWindowsにもインストールできますが、作業は非常に面倒です。pipでインストールしようとすると、以下のエラーが返されます。

Please specify --curl-dir=/path/to/built/libcurl

そのため、ソースからインストールする必要がありますが、これは「多数の依存関係が考えられ、それぞれの依存関係には独自のディレクトリ構造、構成スタイル、パラメータ、癖があるため、気の弱い人には向きません。」 このため、Windowsマシンで作業する場合、基本的なネットワークリクエストにはrequestsパッケージを使用することをお勧めします。

PycURLを使用してリクエストを作成する方法

この記事の残りの部分では、PycURLパッケージを使用したさまざまなタイプのリクエストの作成について詳しく説明します。

PycURLを使用してGETリクエストを作成する

PycURLを使って作成できる最も簡単なリクエストはGETリクエストです。基本的には、このセクションを通じて、他のすべてのテンプレートのテンプレートとなるものです。

以下のコードでは、5つのステップを確認できます。

  1. 必要なパッケージがすべてインポートされます。
  2. 2つのオブジェクト(curlリクエストが応答を保存するバッファと、リクエストの作成に使用されるcurlオブジェクト)が作成されます。
  3. リクエストのオプションとして、URL、宛先、SSL検証が指定されます。
  4. リクエストの実行。
  5. リクエストの出力。
# Preparation
import pycurl
import certifi
from io import BytesIO

# Set buffer and Curl object.
buffer = BytesIO()
c = pycurl.Curl()

# Set request options.
## Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/')

## Set the buffer as the destination of the request's response.
c.setopt(c.WRITEDATA, buffer)

## Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
c.perform()
c.close()

# Print the buffer's content with a Latin1 (iso-8859-1) encoding.
body = buffer.getvalue()
data = body.decode('iso-8859-1')
print(data)

PycURLを使用してPOSTリクエストを作成する

PycURLを使用してPOSTリクエストを作成することは、GETリクエストを作成することによく似ています。ただし、リクエストには1つの追加オプション、つまりPOST本体が追加されます。以下のコードスニペットではキーと値が設定され、それが適切に処理されるようにURLエンコードされています。

# Preparation
import pycurl
import certifi
from io import BytesIO

# Set buffer and Curl object.
buffer = BytesIO()
c = pycurl.Curl()

# Set request options.
## Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/')

## Set the request's body.
post_body = {'greeting': 'hello'}
postfields = urlencode(post_body)
c.setopt(c.POSTFIELDS, postfields)

## Set the buffer as the destination of the request's response.
c.setopt(c.WRITEDATA, buffer)

## Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
c.perform()
c.close()

# Print the buffer's content with a Latin1 (iso-8859-1) encoding.
body = buffer.getvalue()
print(body.decode('iso-8859-1'))

PycURLを使用してPUTリクエストを作成する

前のセクションで作成したPOSTリクエストは、PUTリクエストとして送信することも可能です。キーと値をリクエスト本体で送信する代わりに、UTF-8でエンコードされたファイル表現として送信します。この方法は、ファイルのアップロードにも使用できます。

import pycurl
import certifi
from io import BytesIO

c = pycurl.Curl()

# Set request options.
## Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/')

## Set data for the PUT request.
c.setopt(c.UPLOAD, 1)
data = '{"greeting": "hello"}'
buffer = BytesIO(data.encode('utf-8'))
c.setopt(c.READDATA, buffer)

## Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
c.perform()
c.close()

PycURLを使用してファイルをダウンロードする

次のスニペットは、PycURLを使用してファイルをダウンロードする方法を示しています。ランダムなJPEG画像がリクエストされ、some_image.jpgへの書き込みストリームが開かれ、ファイルの宛先としてPycURLに渡されます。

import pycurl
import certifi

c = pycurl.Curl()

# Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/some_image.jpg')

# Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
with open('some_image.jpg', 'w') as f:
    c.setopt(c.WRITEFUNCTION, f.write)
    c.perform()

c.close()

PycURLを使用してウェブページをダウンロードして処理する

PycURLのユースケースの多くはウェブスクレイピングを伴うため、次のスニペットでは、HTMLファイルを解析する人気のパッケージであるBeautiful Soupを使ってリクエストの応答を処理する方法を説明します。

まず、pipを使ってBeautiful Soup 4をインストールします。

pip install beautifulsoup4

次に、GETリクエストを行った最初のPycURLスニペットのすぐ後ろに、次のスニペットを配置します。これにより、Beautiful Soupが応答データを処理することになります。

デモとして、find_allメソッドを使用してすべての段落要素を検索し、個々の段落の内容を出力します。

from bs4 import BeautifulSoup

# Parsing data using BeautifulSoup
soup = BeautifulSoup(data, 'html.parser')

# Find all paragraphs
paragraphs = soup.find_all('p')
for p in paragraphs:
   print(p.text)

PycURLでプロキシを使用する

ウェブスクレイピングを大規模に行うには、プロキシを使った作業が最適です。利点は、スクレイパーがボットとして、あるいは異常な動作をしているとフラグを立てられることなく、並行してブラウジング動作をエミュレートできることです。

最後のセクションでは、プロキシ経由でPycURLを使用してリクエストを作成する方法を説明します。これは、前回と同様、リクエストオプションを調整することで実現できます。その後、4つの設定について説明しますが、これは状況に応じて調整できます。

  1. これを簡単にするため、安全でないプロキシが有効になっています。
  2. プロキシサーバーが設定されています。
  3. スクリプトがサーバーに対して認証を行います。
  4. プロキシはHTTPSとして設定されています。
# Enable insecure proxies
c.setopt(c.PROXY_SSL_VERIFYHOST, 0)
c.setopt(c.PROXY_SSL_VERIFYPEER, 0)

# Set proxy server
c.setopt(pycurl.PROXY, <YOUR_HTTPS_PROXY_SERVER>)

# Authenticate with the proxy server
c.setopt(pycurl.PROXYUSERPWD, f"{<YOUR_USERNAME>}:{<YOUR_PASSWORD>}")

# Set proxy type to https
c.setopt(pycurl.PROXYTYPE, 2)

これらのオプションは、先に説明したコードスニペット内の任意の場所に挿入して、リクエストをプロキシサーバー経由で再ルーティングさせることができます。

まとめ

この記事では、curlとPythonの組み合わせについて詳しく解説し、ウェブスクレイピングやアプリケーションテストのユースケースで複雑なリクエストを生成する上で、この2つを組み合わせる理由にスポットを当てました。多数のネットワークリクエストを生成するPycURLの多用途性を示すために、複数の例を提示しました。

あるいは、Bright Dataプロキシネットワーク、および開発者の負担を軽減するために特別に設計されたWeb Scraper IDEを利用することもできます。そうすることで、スクレイピング防止メカニズムの回避について煩わされることなく、スクレイピングしたデータの処理に集中できます。