Python クローラー - ソケット モジュールを使用したイメージのダウンロード
ソケットとは何ですか
ソケットは、ネットワーク上のプロセス間通信のための通信メカニズムです。これはアプリケーション層プロトコルであり、通常は TCP/IP プロトコル スタックに基づいており、異なるコンピュータ間の通信を可能にします。ソケットは本質的にファイル記述子であり、ネットワーク通信用の一連の API インターフェイスを提供し、データ送信、接続確立、ポート監視などの操作を実行できます。
Socketを使用すると、クライアントとサーバー間の通信など、異なるコンピュータ間のプロセス間通信が実現できるほか、同一コンピュータ内の異なるプロセス間の通信も実現できます。ソケットはTCPやUDPなどのさまざまな伝送プロトコルをサポートでき、ニーズに応じて通信にさまざまなプロトコルを選択できます。
Python では、ソケット モジュールを使用してソケット プログラミングを実装し、ネットワーク通信用のクライアント プログラムとサーバー プログラムを作成します。ソケット プログラミングは、Web クローラー、チャット ルーム、ファイル転送などのネットワーク アプリケーションの開発に使用できます。
方法 | 説明 |
---|---|
connect( (ホスト,ポート) ) | host はサーバーのホスト名または IP を表し、port はサーバー プロセスにバインドされたポート番号を表します。 |
送信 | リクエストメッセージを送信する |
受信 | データを受信する |
クローラーのワークフロー
クローラーのワークフロー
- リソースアドレスを取得します。
クローラーが最初に行う必要があるのは、データのリソース アドレスを取得することです。正確なアドレスを取得して初めて、データを送信してリクエストを送信できます。
- リクエストを送信してデータを取得する
2 番目のステップは Web ページを取得することです。ここでは Web ページのソース コードを取得します。ソース コードには Web ページの有用な情報が含まれているため、ソース コードを入手すれば、そこから必要な情報を抽出できます。
- アンチクローラ処理:
ウェブサイトによっては、アクセス頻度の制限や認証コードの設定など、クローラー対策が行われている場合があり、クローラープログラムが正常に動作するためには、これらの対策が必要です。クローラー対策はPythonのキャプチャ認識などの技術を利用して対応できます。
- OK:
Web ページのソース コードを取得したら、次のステップは Web ページのソース コードを分析し、そこから必要なデータを抽出することです。まず、最も一般的な方法は正規表現抽出を使用することです。これは汎用的な方法ですが、正規表現を作成する場合はより複雑でエラーが発生しやすくなります。さらに、Web ページの構造には特定の規則があるため、Web ページのノード属性、CSS セレクター、または XPath に基づいて Web ページ情報を抽出するためのライブラリ (Beautiful Soup、pyquery、lxml など) もいくつかあります。これらのライブラリを使用すると、Web ページからノードの属性やテキスト値などの情報を効率的かつ迅速に抽出できます。情報の抽出はクローラーにとって非常に重要な部分であり、後でデータを処理して分析できるように、乱雑なデータを明確にすることができます。
- セーブデータ:
情報を抽出した後、通常、抽出したデータは後で使用できるようにどこかに保存されます。ここでの保存方法はさまざまで、単純にTXTテキストやJSONテキストとして保存することもできますし、MySQLやMongoDBなどのデータベースに保存することもできますし、リモートサーバーなどに保存することもできます。 SFTP で動作する場合と同じです。
- クローラー制御:
クローラー プログラムは、ターゲット Web サイトへの過剰な負荷を避けるために、クロールされる Web ページの数と頻度を制御する必要があります。Python のマルチスレッドまたはマルチプロセスを使用して同時クロールを実現できます。また、時間制御を使用してクロールの頻度を制御することもできます。
- データ分析:
データを取得した後、データの視覚化、機械学習、自然言語処理などのデータ分析と処理を実行して、より価値のある情報を取得できます。
ソケットクローリングの写真
ソケットを使用して写真をダウンロードできるのはなぜですか
Socket を使用して画像をダウンロードできる理由は、HTTP プロトコルが Socket に基づくアプリケーション層プロトコルであり、TCP/IP プロトコル ファミリを使用してデータを送信するためです。HTTP プロトコルでは、クライアントは Socket を介してサーバーへの接続を確立し、リクエストを送信します。サーバーはリクエストを受信して応答を返し、クライアントは応答を受信して応答データを処理します。
HTTPプロトコルはSocketをベースとしているため、Socketを利用して直接HTTPリクエストを送信し、HTTPレスポンスを受信することでデータの送信やダウンロードを実現できます。Socket を使用して画像をダウンロードするには、HTTP 要求の手動構築と HTTP 応答の解析が必要であり、データ送信とエラー処理を処理するためにより多くのコードを記述する必要がありますが、他のダウンロード方法と比較して、Socket の使用はより柔軟でカスタマイズでき、より多くの機能とアプリケーション シナリオを実現します。
ソケットによる画像のダウンロードとリクエストによる画像のダウンロードの違い
画像のダウンロードには Socket と Request の両方を使用できますが、実装方法と目的は少し異なります。
ソケットは、データ送信のためにアプリケーション層とトランスポート層の間の接続を確立できる低レベルのネットワーク通信プロトコルです。Socket を使用して画像をダウンロードするには、HTTP リクエストの手動構築とHTTP レスポンスの解析が必要であり、データ送信とエラー処理を処理するためにさらに多くのコードを記述する必要があります。ソケットは、カスタム プロトコルの実装やオンライン ゲームなど、低レベルのネットワーク通信アプリケーションに適しています。
Request は、HTTP リクエストとレスポンスの処理をカプセル化する Python ライブラリで、ネットワーク リクエストを簡単に作成できるようにします。リクエストを使用して画像をダウンロードするには、簡単なコードのみを必要とし、リクエスト ヘッダー、リクエスト パラメーター、その他の情報を簡単に設定できます。Request は、Web クローラーやデータ収集などのアプリケーションの開発に適しています。
一般に、Socket を使用して画像をダウンロードすることは、より低レベルで柔軟性がありますが、より多くのコードが必要になります。Request を使用して画像をダウンロードすることは、より高レベルで便利ですが、いくつかの制限がある場合があります。どの方法を使用するかは、特定の要件とアプリケーションのシナリオによって異なります。
コードの例:
ソケットを使用して画像をダウンロードします。
import socket
# 构造 HTTP 请求
request = b"GET /images/test.jpg HTTP/1.1\r\nHost: example.com\r\n\r\n"
# 建立连接并发送请求
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("example.com", 80))
s.send(request)
# 接收响应并保存图片
response = s.recv(4096)
while response:
if b"Content-Type: image/jpeg" in response:
with open("test.jpg", "wb") as f:
f.write(response)
break
response = s.recv(4096)
# 关闭连接
s.close()
リクエストを使用して写真をダウンロードします。
import requests
# 发送请求并保存图片
response = requests.get("http://example.com/images/test.jpg")
with open("test.jpg", "wb") as f:
f.write(response.content)
写真をダウンロードするために Request を使用する方が簡単で便利ですが、Socket を使用すると、HTTP リクエストの手動構築と HTTP 応答の解析が必要となり、より多くのコードと処理が必要になることがわかります。
ソケットを使用して画像をダウンロードする
http://image11.m1905.cn/uploadfile/2021/0922/thumb_0_647_500_20210922030733993182.jpg
たとえば写真を撮ります。
ソケットを使用してこの画像をダウンロードする具体的な手順は次のとおりです。
-
リソースアドレスのURLを取得します。
-
ソケット クライアント オブジェクト client を作成し、サーバー image11.m1905.cn のポート 80 に接続します。
-
リクエスト メソッド、リクエスト アドレス、リクエスト プロトコル バージョン、リクエスト ヘッダー、その他の情報を含む HTTP リクエストを作成し、リクエストを送信します。
-
このループはサーバー応答を受信し、応答データをバイナリ オブジェクトの結果に追加します。
-
正規表現を使用して、応答データ内の画像データを抽出します。つまり、応答ヘッダーを削除します。
-
画像データをローカル ファイルに保存します。つまり、画像をローカルにダウンロードします。
コードは以下のように表示されます。
import socket
import re
import time
# 获取到资源地址
url = 'http://image11.m1905.cn/uploadfile/2021/0922/thumb_0_647_500_20210922030733993182.jpg'
start = time.time()
# 创建套接字对象
client = socket.socket()
# 创建连接
client.connect(('image11.m1905.cn', 80))
# 构造http请求
http_res = 'GET ' + url + ' HTTP/1.0\r\nHost: image11.m1905.cn\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\r\n\r\n'
# print(http_res)
# 发送请求
client.send(http_res.encode())
# 建立一个二进制对象用来存储我们得到的数据
result = b''
data = client.recv(1024)
# 循环接收响应数据 添加到bytes类型
while data:
result += data
data = client.recv(1024)
# print(result)
# 提取数据
# re.S使 . 匹配包括换行在内的所有字符去掉响应头
images = re.findall(b'\r\n\r\n(.*)', result, re.S)
# print(images[0])
# 打开一个文件,将我们读取到的数据存入进去,即下载到本地我们获取到的图片
with open('小姐姐.png', 'wb')as f:
f.write(images[0])
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start # 计算代码执行时间
print(f'All images downloaded successfully in {elapsed_time:.2f} seconds')
ソケットを使用して複数の写真をダウンロードする
ソケットを使用して複数の画像をダウンロードしたいと仮定して、ここでは 2 つの実装方法を示します。
方法1
ダウンロードする必要がある URL のリストを作成し、split 関数を使用して/
URL を分割し、スライスして URL 内の相対パスとホスト値を取得し、最後に URL リストをループして複数のダウンロードの効果を実現します。ピクチャー。コードは以下のように表示されます
import socket
import re
import time
start = time.time()
# 获取到的资源地址
urls = [
'https://pic.netbian.com/uploads/allimg/220211/004115-1644511275bc26.jpg',
'https://pic.netbian.com/uploads/allimg/220215/233510-16449393101c46.jpg',
'https://pic.netbian.com/uploads/allimg/211120/005250-1637340770807b.jpg'
]
# 创建连接
for url in urls:
# 解析URL
parts = url.split('/') # ['https:', '', 'pic.netbian.com', 'uploads', 'allimg', '220211', '004115-1644511275bc26.jpg']
host = parts[2] # pic.netbian.com
path = '/' + '/'.join(parts[3:]) # /uploads/allimg/220211/004115-1644511275bc26.jpg
# print(parts)
# print(host)
# print(path)
# 创建套接字对象连接到主机
client = socket.socket()
client.connect((host, 80))
# 构造HTTP请求
http_req = f'GET {path} HTTP/1.1\r\nHost: {host}\r\nuser-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' \
f'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36\r\nConnection: close\r\n\r\n '
# print(http_req)
# 发送请求
client.sendall(http_req.encode())
# 接受响应数据
result = b''
data = client.recv(1024)
while data:
result += data
data = client.recv(1024)
# 提取图像数据
images = re.findall(b'\r\n\r\n(.*)', result, re.S)
# print(images[0])
# 写入文件
if images:
with open(f'{parts[-1]}', 'wb') as f:
f.write(images[0])
else:
print(f'No image data received for {url}')
# 关闭连接
client.close()
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start # 计算代码执行时间
print(f'All images downloaded successfully in {elapsed_time:.2f} seconds')
方法 2
スレッド プールを作成すると、マルチスレッド方式ですべての画像を同時にダウンロードできます。
コードは以下のように表示されます。
import socket
import re
import multiprocessing
import time
def download_image(url):
# 解析URL
parts = url.split('/')
host = parts[2]
path = '/' + '/'.join(parts[3:])
# 创建套接字对象连接到主机
client = socket.socket()
client.connect((host, 80))
# 构造HTTP请求
http_req = f'GET {path} HTTP/1.1\r\nHost: {host}\r\nuser-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' \
f'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36\r\nConnection: close\r\n\r\n '
# 发送请求
client.sendall(http_req.encode())
# 接受响应数据
result = b''
data = client.recv(1024)
while data:
result += data
data = client.recv(1024)
# 提取图像数据
images = re.findall(b'\r\n\r\n(.*)', result, re.S)
# 写入文件
if images:
with open(f'{parts[-1]}', 'wb') as f:
f.write(images[0])
else:
print(f'No image data received for {url}')
# 关闭连接
client.close()
if __name__ == '__main__':
start = time.time()
urls = [
'https://pic.netbian.com/uploads/allimg/220211/004115-1644511275bc26.jpg',
'https://pic.netbian.com/uploads/allimg/220215/233510-16449393101c46.jpg',
'https://pic.netbian.com/uploads/allimg/211120/005250-1637340770807b.jpg'
]
# 创建进程池
pool = multiprocessing.Pool(processes=3)
# 同时下载所有图片
pool.map(download_image, urls)
# 关闭进程池
pool.close()
pool.join()
end = time.time()
elapsed_time = end - start # 计算代码执行时间
print(f'All images downloaded successfully in {elapsed_time:.2f} seconds')