質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

HTTPS

HTTPS(Hypertext Transfer Protocol Secure)はHypertext Transfer プロトコルとSSL/TLS プロトコルを組み合わせたものです。WebサーバとWebブラウザの間の通信を暗号化させて、通信経路上での盗聴や第三者によるなりすましを防止します。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

2回答

4314閲覧

Python3のsocketライブラリを使ってhttpリクエストをしたい

退会済みユーザー

退会済みユーザー

総合スコア0

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

HTTPS

HTTPS(Hypertext Transfer Protocol Secure)はHypertext Transfer プロトコルとSSL/TLS プロトコルを組み合わせたものです。WebサーバとWebブラウザの間の通信を暗号化させて、通信経路上での盗聴や第三者によるなりすましを防止します。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2019/01/04 18:19

前提・実現したいこと

Python3のsocketライブラリを使ってhttpリクエストをしたいです(requestライブラリを使えば簡単というのは知っていますが、こっちでもやってみようということで)。

発生している問題・エラーメッセージ

"400 Bad Request"のhtmlが返ってきます。

該当のソースコード

Python3

1import socket 2 3mes='GET / HTTP1.1\r\n' 4mes+='Host: "qiita.com\r\n' 5mes+='Connection: close\r\n' 6mes+='\r\n' 7 8s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 9s.connect(("qiita.com",80)) 10 11s.send(mes.encode("utf-8")) 12 13received=s.recv(1024).decode("utf-8") 14 15s.shutdown(socket.SHUT_RDWR) 16s.close() 17 18print(received)

試したこと

・yahoo.co.jpで同じことをすると正しいhtmlが返ってきます。

・(これ以下はもしかすると全く見当違いのことを喋っているのかもしれませんが...)
teratail.comで同じことをすると"400 Bad Request"が返ってくるのですが、レスポンスヘッダの一つに

Referrer-Policy: no-referrer

というのがありました。httpsのポートにリクエストを送ればいいのか?と考え、ポート番号を443にしてみたうえで、 https://teratail.com/questions/44137 を参考にさせて頂き、URLの先頭にhttps://をつけてみると今度は

Traceback (most recent call last): File "hoge.py", line 9, in <module> s.connect(("https://qiita.com",443)) socket.gaierror: [Errno 11001] getaddrinfo failed

というエラーが飛んできます。

宜しくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

車輪の再発明をやってみるのは良いことですね。私ならこう書きます。

python

1from contextlib import closing 2from socket import create_connection 3from ssl import wrap_socket 4 5req = (b"GET / HTTP/1.1\r\n" 6 b"Host: qiita.com\r\n" 7 b"\r\n") 8 9with closing(create_connection(("qiita.com", 443))) as s: 10 ss = wrap_socket(s) 11 ss.send(req) 12 print(ss.recv(1024).decode())
  • mesの内容が変だった部分を修正しました。
  • b"..."の形式でリクエストを記述すれば送信時にエンコードが不要なのでそうしてます。
  • 個人的に+で文字列をくっつけていくのは好きではないのでカッコで文字列をくっつけた風に書いてます。
  • create_connection()だとIPv4でもIPv6でもうまいことやってくれるのでそっちを使ってます。
  • withclosing()create_connection()がオープンしたコネクションを確実にクローズするようにします。
  • wrap_socketを使って443へのコネクションをSSL/TLSへアップグレードしてます。

投稿2019/01/05 03:50

編集2019/01/05 03:52
YouheiSakurai

総合スコア6142

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/01/05 11:51

HTTPバージョンの記述が間違ってたのは完全に盲点でした。これは自分で気づかないと... なるほど単にポート番号を変えるだけでなくて、リクエストをSSL暗号化しておかないとダメなんですね 勉強になりました。
guest

0

socket.connectの引数はIPアドレスや名前解決が可能なホスト名です。今回ご質問の場合はスキーム名httpsを含んだURLにしてしまっているため、socket.getaddrinfoでエラーが発生しています。ここはあくまでs.connect(("qiita.com", 443))と指定しなければいけません。

しかしながらそこだけ直しても400 Bad Requestが返ります。GETメソッドで指定しているHTTPバージョン文字列の記述が間違っている為です。"HTTP1.1"ではなく、"HTTP/1.1"と指定するのが正しいです。yahoo.co.jpではHTTPステータス200で返ってくるのは、単に許容範囲が広いかノーチェックなだけだと思われます。

そんなこんなで直すと、結局以下のようなコードになります。実質、2行違うだけです。

Python3

1import socket 2 3mes = 'GET / HTTP/1.1\r\n' 4mes += 'Host: qiita.com\r\n' 5mes += 'Connection: close\r\n' 6mes += '\r\n' 7 8s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 9s.connect(("qiita.com", 443)) 10 11s.send(mes.encode("utf-8")) 12 13received = s.recv(1024).decode("utf-8") 14 15s.shutdown(socket.SHUT_RDWR) 16s.close() 17 18print(received)

残念ながらこれでも結局 400 Bad Request が返ります。しかしこれまでとは違い、コンテントボディ部分が以下のように変わります。

$ python3 t1c.py HTTP/1.1 400 Bad Request Server: awselb/2.0 Date: Sat, 05 Jan 2019 02:38:27 GMT Content-Type: text/html Content-Length: 236 Connection: close <html> <head><title>400 The plain HTTP request was sent to HTTPS port</title></head> <body bgcolor="white"> <center><h1>400 Bad Request</h1></center> <center>The plain HTTP request was sent to HTTPS port</center> </body> </html>

htmlのタイトル部分にもボディ部分にも、「The plain HTTP request was sent to HTTPS port」と説明されています。HTTPSなサーバーに平文なGETリクエストを送りつけたからでしょう。HTTPSでやり取りするならば正規な手順を経て暗号化したリクエストとレスポンスの送受信をしなければなりません。これはsocket, send, recvなどでは不可能です。

httpでアクセス可能なサイトかまたは素直にurllibを使うほかありません。ただ、urllibを読み解けばsocket, send, recv等は恐らく使われているであろうから、それらを参考にして自力でコードを書くことはできるかもしれません。(未確認です)

最後に、他人様のサイトに不正なリクエストを何度も送るのは決して良いことではないのでその点は注意しましょう。

投稿2019/01/05 03:03

dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/01/05 11:54

HTTPバージョンの記述が間違ってたのは完全に盲点でした。これは自分で気づかないと... なるほど単にポート番号を変えるだけでなくて、リクエストをSSL暗号化しておかないとダメなんですね 他とても詳しく説明して頂き非常に参考になりました、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問