Я пытаюсь программно отправить некоторые данные в форму на нашей странице администрирования нашей компании, а не делать это вручную.
Я написал множество других инструментов, которые очищают этот сайт и манипулируют данными. Однако по какой-то причине этот конкретный вопрос дает мне тонну проблемы.
Прогулка через браузер:
Ниже приведены страницы, которые я пытаюсь очистить и отправить данные. Обратите внимание, что эти страницы обычно отображаются в js shadowboxes, однако он отлично работает с отключенным Javascript, поэтому я предполагаю, что javascript не является проблемой в отношении проблемы скребка.
(Обратите внимание: поскольку это страница компании, я заполнил, я заменил все поля формы на нежелательные заголовки, так что, например, номера клиентов полностью созданы)
Кроме того, поскольку это страница компании за стеной имени пользователя и пароля, я не могу выдавать веб-сайт для тестирования, поэтому я попытался внести как можно больше подробностей в этот пост!
Главная точка входа находится здесь:
На этой странице я нажимаю "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
. Но здесь, где он становится вялым. Ответ, который возвращает механизация, - это та же самая страница с формой. Нет сообщений об ошибках, сообщений об ошибках и когда я вручную проверяю нашу страницу администратора, никаких изменений не было сделано.
Проверка сетевой активности
Разочарованный, я открыл Chrome и наблюдал за вкладкой в сети, когда я вручную подал заявку и отправил форму в браузере.
При отправке формы это сетевая активность:
Похоже на меня. Там post
, а затем a get
для css файлов и еще один get
для библиотеки jquery. Там есть еще get
для какого-то изображения, но я понятия не имею, для чего это.
Проверка деталей запроса POST:
После некоторого Googling о проблемах с выскабливанием, я увидел предположение, что сервер может ожидать определенный заголовок, и я должен просто скопировать все, что делается в запросе POST, а затем медленно отнять заголовки, пока не выясню, какой из них был важным. Поэтому я сделал именно это, скопировал каждый бит информации на вкладке "Сеть" и застрял в моем почтовом запросе.
Попытка кода 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", в запрос POST в urllib2
.
Одно из изменений в версии Mechainze заключается в том, что я теперь напрямую обращаюсь к странице add_form.php
, добавив соответствующий куки файл в свой запрос.
Однако даже при дублировании все, что я могу, у меня все еще есть одна и та же проблема: ответ - это та же самая страница, на которой я начал: никаких ошибок, сообщений об успешном выполнении, никаких изменений на сервере, просто вернулся к пустому форма.
Заключительный шаг: Отчаяние сидит, я устанавливаю WireShark
Время, чтобы сделать некоторое обнюхивание трафика. Я полна решимости видеть, что WTF происходит в этом магическом письме!
Я загружаю, устанавливаю и запускаю Wireshark. Я фильтрую для http
, а затем сначала отправлю форму вручную в браузере, а затем запустите мой код с попытками отправить форму программно.
Это сетевой трафик:
Браузер:
Python:
Помимо того, что заголовки находятся в несколько ином порядке (это имеет значение), они выглядят точно так же!
Итак, где я, совершенно смущен, почему запрос post
, который (насколько я могу судить), почти идентичный тому, который сделан браузером, не вносит никаких изменений на сервер.
Кто-нибудь когда-нибудь сталкивался с чем-либо подобным? Мне что-то не хватает? Что здесь происходит?
Изменить
В соответствии с предложением Рика я точно воспроизвел данные post
. Я копирую его прямо со вкладки "Источник сети" в Chrome.
Измененный код выглядит следующим образом
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
.
К сожалению, это все равно дает тот же результат. Ответ - та же самая страница, с которой я начал.
Update
работает, но не решен
Я проверил библиотеку requests
, продублировал свои усилия с помощью их API, и вот это волшебство! Пост действительно прошел. Остается вопрос: почему!? Я снова сделал еще один снимок с wirehark, и, насколько я могу судить, он точно такой же, как 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'
Это дает cookie ID
для моего 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 существует, и он был отправлен вместе с запросом. Так что все еще не уверен, что происходит.