手作業ではなく、当社の管理ページのフォームにデータをプログラムで送信しようとしています。
私は、この Web サイトをスクレイピングしてデータを操作するツールを他にも数多く作成しました。しかし、何らかの理由で、この特定の 1 つが私に多くの問題を引き起こしています。
ブラウザでのウォークスルー:
以下は、データをスクレイピングして投稿しようとしているページです。これらのページは通常 js シャドウ ボックスに表示されますが、Javascript を無効にしても問題なく機能することに注意してください。
(注、これは会社のページなので、すべてのフォーム フィールドをジャンク タイトルに置き換えました。たとえば、顧客番号は完全にでっち上げです)
また、ユーザー名/パスワードの壁の背後にある会社のページであるため、テスト用に Web サイトを提供することはできません。そのため、この投稿にできるだけ多くの詳細を挿入しようとしました!
主なエントリーポイントはこちら:
このページから をクリック"Add New form"
すると、次のページが新しいタグで開きます (javascript が無効になっているため)。
このページで、小さなフォームに入力し、[送信] をクリックすると、次のページに成功メッセージが表示されます。
シンプルなはずですよね?
コード試行 1: 機械化
import mechanize
import base64
import cookielib
br = mechanize.Browser()
username = 'USERNAME'
password = 'PASSWORD'
br.addheaders.append(('Authorization',
'Basic %s' % base64.encodestring('%s:%s' % (username, password))))
br.addheaders = [('User-agent',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.22 (KHTML,'
' like Gecko) Chrome/25.0.1364.172 Safari/537.22')]
br.open('www.our_company_page.com/adm/add_forms.php')
links = [link for link in br.links()]
# Follow "Add a form" Link
response = br.follow_link(links[0])
br.select_form(nr=0)
br.form.set_all_readonly(False)
br.form['formNumber'] = "FROM_PYTHON"
br.form['RevisionNumber'] = ['20']
br.form['FormType'] = ['H(num)']
response = br.submit()
print response.read() #Shows the exact same page! >:(
ご覧のとおり、ブラウザーで実行する手順を再現しようとしています。最初のページを読み込み、/adm/forms
最初のリンク ( ) をたどり、Add a Form
フォームに入力して、submit
ボタンをクリックします。しかし、ここが厄介なところです。mechanize が返す応答は、フォームとまったく同じページです。エラー メッセージも成功メッセージも表示されず、管理ページを手動で確認しても変更はありません。
ネットワーク アクティビティの検査
イライラして、私は Chrome を開いてネットワーク タブを見ながら、手動でフォームに入力してブラウザで送信しました。
フォームを送信すると、ネットワーク アクティビティは次のようになります。
私にはかなり簡単に思えます。、css ファイル用の 、およびjquerypost
ライブラリ用の があります。ある種の画像用に別のものがありますが、それが何のためにあるのかわかりません。get
get
get
POST リクエストの詳細を調べる:
スクレイピングの問題についてグーグルで調べた後、サーバーが特定のヘッダーを予期している可能性があるという提案を見ました。POST リクエストで作成されたものをすべてコピーし、どれが重要であるかがわかるまでゆっくりとヘッダーを削除する必要があります。 1。だから私はまさにそれを行い、[ネットワーク] タブのすべての情報をコピーして、投稿リクエストに貼り付けました。
コード試行 2: Urllib
ですべてのヘッダーを把握するのに苦労したMechanize
ので、urllib2 に切り替えました。
import urllib
import urllib2
import base64
url = 'www.our_company_page.com/adm/add_forms.php'
values = {
'SID':'', #Hidden field
'FormNumber':'FROM_PYTHON1030PM',
'RevisionNumber':'5',
'FormType':'H(num)',
'fsubmit':'Save Page'
}
username = 'USERNAME'
password = 'PASSWORD'
headers = {
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'Accept-Encoding' : 'gzip,deflate,sdch',
'Accept-Language' : 'en-US,en;q=0.8',
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Authorization': 'Basic %s' % base64.encodestring('%s:%s' % (username, password)),
'Cache-Control' : 'max-age=0',
'Connection' : 'keep-alive',
'Content-Type' : 'application/x-www-form-urlencoded',
'Cookie' : 'ID=201399',
'Host' : 'our_company_page.com',
'Origin' : 'http://our_company_page.com',
'Referer' : 'http://our_company_page.com/adm/add_form.php',
'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, '
'like Gecko) Chrome/26.0.1410.43 Safari/537.31'
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
print response.read()
ご覧のとおり、Chrome の Network タブにあるヘッダーを の POST リクエストに追加しましたurllib2
。
Mechainze バージョンからの追加の変更点の 1 つはadd_form.php
、関連する Cookie をリクエストに追加することで、ページに直接アクセスできるようになったことです。
ただし、可能な限りすべてを複製しても、まったく同じ問題が発生します。応答は、開始したページとまったく同じです。エラーも成功メッセージも、サーバーでの変更もなく、空白のフォームに戻っただけです。
最後のステップ: 必死になって、WireShark をインストールします
トラフィック スニッフィングを行う時間です。この魔法のような投稿リクエストで WTF が行われていることを確認したいと思います!
Wireshark をダウンロードしてインストールし、起動します。をフィルタリングしてhttp
から、まずブラウザでフォームを手動で送信し、コードを実行してフォームをプログラムで送信しようとします。
これはネットワーク トラフィックです。
ブラウザ:
パイソン:
ヘッダーの順序が少し異なることを除けば (それは問題ではありません)、それらはまったく同じに見えます!
それで、私が完全に混乱しているのpost
は、(私が知る限り)ブラウザによって行われたものとほぼ同じであるリクエストが、サーバー上で変更を加えていない理由です。
誰もこのようなことに遭遇したことがありますか?明らかな何かが欠けていますか?何が起きてる?
編集
Ric の提案に従って、POST
データを正確に複製しました。Chrome の Network Source タブから直接コピーします。
変更されたコードは次のようになります
data = 'SegmentID=&Segment=FROMPYTHON&SegmentPosition=1&SegmentContains=Sections&fsubmit=Save+Page'
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
print response.read()
私が変更したのは、からへのSegment
値だけでした。FROMBROWSER
FROMPYTHON
残念ながら、これでも同じ結果が得られます。応答は、私が開始したのと同じページです。
アップデート
動作していますが、解決されていません
私はライブラリをチェックアウトし、requests
彼らの API を使用して自分の努力を複製したところ、魔法のように機能しました! POSTは実際に通過しました。疑問が残ります:なぜ!? 私は再びwiresharkで別のスナップショットを撮りました.ブラウザから作成されたPOSTとまったく同じであることがわかります.
コード
def post(eventID, name, pos, containsID):
segmentContains = ["Sections", "Products"]
url = 'http://my_site.com/adm/add_page.php'
cookies = dict(EventID=str(eventID))
payload = { "SegmentID" : "",
"FormNumber" : name,
"RevisionNumber" : str(pos),
"FormType" : containsID,
"fsubmit" : "Save Page"
}
r = requests.post(
url,
auth=(auth.username, auth.password),
allow_redirects=True,
cookies=cookies,
data=payload)
Wireshark 出力
リクエスト
ブラウザ
それで、質問の現在の状態を要約します。それは機能しますが、実際には何も変わっていません。Mechanize と urllib2 の両方で失敗した理由がわかりません。requests
そのPOST が実際に通過できるようにするために何が起こっているのでしょうか?
編集 -- Wing Tang Wong の提案:
Wing Tand Wongs
提案に応じて、Cookie ハンドラーを作成し、それをurllib.opener
. そのため、ヘッダーで Cookie を手動で送信することはもうありません。実際、今は何も割り当てていません。
すぐにフォームに接続するのではなく、最初にフォームへのリンクを使用してadmページに接続します。
'http://my_web_page.com/adm/segments.php?&n=201399'
これにより、ID
Cookie が myに渡されますurllib
cookieJar
。この時点から、フォームのあるページへのリンクをたどり、通常どおり送信を試みます。
完全なコード:
url = 'http://my_web_page.com/adm/segments.php?&n=201399'
post_url = 'http://my_web_page.com/adm/add_page.php'
values = {
'SegmentID':'',
'Segment':'FROM_PYTHON1030PM',
'SegmentPosition':'5',
'SegmentContains':'Products',
'fsubmit':'Save Page'
}
username = auth.username
password = auth.password
headers = {
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'Accept-Encoding' : 'gzip,deflate,sdch',
'Accept-Language' : 'en-US,en;q=0.8',
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Authorization': 'Basic %s' % base64.encodestring('%s:%s' % (username, password)),
'Cache-Control' : 'max-age=0',
'Connection' : 'keep-alive',
'Content-Type' : 'application/x-www-form-urlencoded',
'Host' : 'mt_site.com',
'Origin' : 'http://my_site.com',
'Referer' : 'http://my_site.com/adm/add_page.php',
'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31'
}
COOKIEFILE = 'cookies.lwp'
cj = cookielib.LWPCookieJar()
if os.path.isfile(COOKIEFILE):
cj.load(COOKIEFILE)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
urllib2.install_opener(opener)
data = urllib.urlencode(values)
req = urllib2.Request(url, headers=headers)
handle = urllib2.urlopen(req)
req = urllib2.Request(post_url, data, headers)
handle = urllib2.urlopen(req)
print handle.info()
print handle.read()
print
if cj:
print 'These are the cookies we have received so far :'
for index, cookie in enumerate(cj):
print index, ' : ', cookie
cj.save(COOKIEFILE)
前と同じこと。サーバー上で変更は行われません。Cookie が実際に存在することを確認するために、フォームを送信した後にそれらをコンソールに出力すると、次のような出力が得られます。
These are the cookies we have received so far :
<Cookie EventID=201399 for my_site.com/adm>
したがって、Cookie はそこにあり、リクエストと一緒に送信されています。まだ何が起こっているのかわかりません。