2016-04-19 22 views
6

Đây là những gì tôi đang cố gắng làm: đi here, sau đó nhấn "tìm kiếm". Lấy dữ liệu, sau đó nhấn "tiếp theo" và tiếp tục nhấn tiếp theo cho đến khi bạn hết trang. Mọi thứ đều có thể chạm vào các tác phẩm "tiếp theo". Đây là mã của tôi. Định dạng của r.content hoàn toàn khác hai lần tôi in nó, cho biết có điều gì đó khác xảy ra giữa các yêu cầu GET và POST mặc dù tôi muốn có hành vi rất giống nhau. Tại sao điều này có thể xảy ra?Thực hiện yêu cầu POST tiếp theo trong phiên không hoạt động - web scraping

Điều tôi thấy lạ là ngay cả sau khi yêu cầu POST dường như trả lại nội dung sai, tôi vẫn có thể phân tích các url tôi cần - không phải trường nhập __EVENTVALIDATION.

Thông báo lỗi (kết thúc mã) cho biết nội dung không bao gồm dữ liệu này mà tôi cần để thực hiện yêu cầu tiếp theo, nhưng điều hướng đến trang cho thấy rằng nó có dữ liệu đó và định dạng rất giống với trang đầu tiên.

EDIT: Tôi đang mở trang web dựa trên HTML đó là phân tích cú pháp và điều gì đó chắc chắn không đúng. chạy mã bên dưới sẽ mở các trang đó.

Các GET được tôi một trang web với dữ liệu như thế này:

<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="4424DBE6"> 
<input type="hidden" name="__VIEWSTATEENCRYPTED" id="__VIEWSTATEENCRYPTED" value=""> 
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="TlIgNH 

Trong khi POST tạo ra một trang web với tất cả các dữ liệu mà ở dưới cùng của trang trong bản rõ, như thế này:

|0|hiddenField|__EVENTTARGET||0|hiddenField|__EVENTARGUMENT||0|hiddenField|_ 

Bad r.content

Good r.content

import requests 
from lxml import html 
from bs4 import BeautifulSoup 



page = requests.get('http://search.cpsa.ca/physiciansearch') 
print('got page!') 
d = {"ctl00$ctl13": "ctl00$ctl13|ctl00$MainContent$physicianSearchView$btnSearch", 
    "ctl00$MainContent$physicianSearchView$txtLastName": "", 
    'ctl00$MainContent$physicianSearchView$txtFirstName': "", 
    'ctl00$MainContent$physicianSearchView$txtCity': "", 
    "__VIEWSTATEENCRYPTED":"", 
    'ctl00$MainContent$physicianSearchView$txtPostalCode': "", 
    'ctl00$MainContent$physicianSearchView$rblPractice': "", 
    'ctl00$MainContent$physicianSearchView$ddDiscipline': "", 
    'ctl00$MainContent$physicianSearchView$rblGender': "", 
    'ctl00$MainContent$physicianSearchView$txtPracticeInterests': "", 
    'ctl00$MainContent$physicianSearchView$ddApprovals': "", 
    'ctl00$MainContent$physicianSearchView$ddLanguage': "", 
    "__EVENTTARGET": "ctl00$MainContent$physicianSearchView$btnSearch", 
    "__EVENTARGUMENT": "", 
    'ctl00$MainContent$physicianSearchView$hfPrefetchUrl': "http://service.cpsa.ca/OnlineService/OnlineService.svc/Services/GetAlbertaCities?name=", 
    'ctl00$MainContent$physicianSearchView$hfRemoveUrl': "http://service.cpsa.ca/OnlineService/OnlineService.svc/Services/GetAlbertaCities?name=%QUERY", 
    '__ASYNCPOST': 'true'} 

h ={ "X-MicrosoftAjax":"Delta = true", 
"X-Requested-With":"XMLHttpRequest", 
    "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36" 
} 

urls = [] 

with requests.session() as s: 
    r = s.get("http://search.cpsa.ca/PhysicianSearch",headers=h) 
    soup = BeautifulSoup(r.content, "lxml") 
    tree = html.fromstring(r.content) 
    html.open_in_browser(tree) 

    ev = soup.select("#__EVENTVALIDATION")[0]["value"] 
    vs = soup.select("#__VIEWSTATE")[0]["value"] 
    vsg = soup.select("#__VIEWSTATEGENERATOR")[0]["value"] 
    d["__EVENTVALIDATION"] = ev 
    d["__VIEWSTATEGENERATOR"] = vsg 
    d["__VIEWSTATE"] = vs 
    r = s.post('http://search.cpsa.ca/PhysicianSearch', data=d,headers=h) 



    print('opening in browser') 
    retrievedUrls = tree.xpath('//*[@id="MainContent_physicianSearchView_gvResults"]/tr/td[2]/a/@href') 
    print(retrievedUrls) 

    for url in retrievedUrls: 
     urls.append(url) 

    endSearch = False  
    while endSearch == False: 

     tree = html.fromstring(r.content) 
     html.open_in_browser(tree) 


     soup = BeautifulSoup(r.content, "lxml") 
     print('soup2:') 
     ## BREAKS HERE 
     ev = soup.select("#__EVENTVALIDATION")[0]["value"] 
     ## BREAKS HERE, 
     vs = soup.select("#__VIEWSTATE")[0]["value"] 
     vsg = soup.select("#__VIEWSTATEGENERATOR")[0]["value"] 

     d["ctl00$ctl13"] = "ctl00$MainContent$physicianSearchView$ResultsPanel|ctl00$MainContent$physicianSearchView$gvResults$ctl01$btnNextPage" 
     d["__EVENTVALIDATION"] = ev 
     d["__EVENTTARGET"] = "" 
     d["__VIEWSTATEGENERATOR"] = vsg 
     d["__VIEWSTATE"] = vs 
     d["ctl00$MainContent$physicianSearchView$gvResults$ctl01$ddlPager"] = 1 
     d["ctl00$MainContent$physicianSearchView$gvResults$ctl01$ddlPager"] = 1 
     d["ctl00$MainContent$physicianSearchView$gvResults$ctl01$btnNextPage"] = "Next" 
     r = requests.post('http://search.cpsa.ca/PhysicianSearch', data=d,headers=h) 
     tree = html.fromstring(r.content) 
     tree = html.fromstring(r.content) 
     retrievedUrls = tree.xpath('//*[@id="MainContent_physicianSearchView_gvResults"]/tr/td[2]/a/@href') 
     print(urls) 
     print(retrievedUrls) 
     endSearch = True 

... 

Traceback (most recent call last): 
    File "C:\Users\daniel.bak\workspace\Alberta Physician Scraper\main\main.py", line 63, in <module> 
    ev = soup.select("#__EVENTVALIDATION")[0]["value"] 
IndexError: list index out of range 

Trả lời

4

Vâng này gần chở tôi về tinh thần nhưng nó cuối cùng đang làm việc, bạn phải thực hiện một yêu cầu get để có được một __EVENTVALIDATION thẻ mới cho mỗi bài:

import requests 

from bs4 import BeautifulSoup 

h = {"X-MicrosoftAjax": "Delta = true", 
    "X-Requested-With": "XMLHttpRequest", 
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36" 
    } 
"ctl00$ctl13 | ctl00$MainContent$physicianSearchView$btnSearch" 
d = { 
    "ctl00$ctl13": "ctl00$MainContent$physicianSearchView$btnSearch", 
    "__EVENTTARGET": "ctl00$MainContent$physicianSearchView$btnSearch", 
    'ctl00$MainContent$physicianSearchView$hfPrefetchUrl': "http://service.cpsa.ca/OnlineService/OnlineService.svc/Services/GetAlbertaCities?name=", 
    'ctl00$MainContent$physicianSearchView$hfRemoveUrl': "http://service.cpsa.ca/OnlineService/OnlineService.svc/Services/GetAlbertaCities?name=%QUERY", 
    '__ASYNCPOST': 'true'} 

nxt_d = { 
    "ctl00$ctl13": "ctl00$MainContent$physicianSearchView$ResultsPanel|ctl00$MainContent$physicianSearchView$gvResults$ctl14$ddlPager", 
    "ctl00$MainContent$physicianSearchView$gvResults$ctl01$ddlPager": "2", 
    "ctl00$MainContent$physicianSearchView$gvResults$ctl14$ddlPager": "1", 
    "__ASYNCPOST": "true", 
    "__EVENTTARGET": "ctl00$MainContent$physicianSearchView$gvResults$ctl14$ddlPager"} 

url = "http://search.cpsa.ca/PhysicianSearch" 
with requests.session() as s: 
    r = s.get(url, headers=h) 
    soup = BeautifulSoup(r.content, "lxml") 
    ev = soup.select("#__EVENTVALIDATION")[0]["value"] 
    vs = soup.select("#__VIEWSTATE")[0]["value"] 
    d["__EVENTVALIDATION"] = ev 
    d["__VIEWSTATE"] = vs 
    r = s.post(url, data=d, headers=h) 
    soup = BeautifulSoup(s.get("http://search.cpsa.ca/PhysicianSearch").content, "lxml") 
    ev = soup.select("#__EVENTVALIDATION")[0]["value"] 
    vs = soup.select("#__VIEWSTATE")[0]["value"] 
    nxt_d["__EVENTVALIDATION"] = ev 
    nxt_d["__VIEWSTATE"] = vs 
    r = s.post(url, data=nxt_d, headers=h) 

Nếu bạn mở nguồn từ bài viết cuối cùng bạn sẽ thấy bạn nhấn trang 2. Chúng tôi cần thêm nhiều logic hơn để có được thông qua tất cả các trang, tôi sẽ thêm nó trong một chút.

Các params:

"ctl00$MainContent$physicianSearchView$gvResults$ctl01$ddlPager": "2", 
"ctl00$MainContent$physicianSearchView$gvResults$ctl14$ddlPager": "1" 

là trang để đi đến và trang bạn đang đến từ vì vậy sau khi get nên tất cả những gì cần phải thay đổi.

này sẽ nhận được tất cả các trang, kéo hầu hết các giá trị theo chương trình, bạn có thể có thể kéo đặc biệt hơn với sự trợ giúp của một regex nhưng nó kéo hầu hết không có giá trị mã hóa cứng:

from lxml.html import fromstring 
import requests 


class Crawler(object): 
    def __init__(self, ua, url): 
     self.user_agent = ua 
     self.post_header = {"X-MicrosoftAjax": "Delta = true", "X-Requested-With": "XMLHttpRequest", "user-agent": ua} 
     self.post_data2 = {'__ASYNCPOST': 'true', 
          "ctl00$ctl13": "ctl00$MainContent$physicianSearchView$ResultsPanel|ctl00$MainContent$physicianSearchView$gvResults$ctl14$ddlPager"} 
     self.url = url 
     self.post_data1 = { '__ASYNCPOST': 'true'} 

    def populate(self, xml): 
     """Pulls form post data keys and values for initial post.""" 
     k1 = xml.xpath("//*[@id='hfPrefetchUrl']")[0] 
     k2 = xml.xpath("//*[@id='hfRemoveUrl']")[0] 
     self.post_data1[k1.get("name")] = k1.get("value") 
     self.post_data1[k2.get("name")] = k2.get("value") 
     self.post_data1["ctl00$ctl13"] = xml.xpath("//input[@value='Search']/@name")[0] 
     self.post_data1["__EVENTTARGET"] = self.post_data1["ctl00$ctl13"] 

    def populate2(self, xml): 
     """Pulls form post data keys and values, 
      for all subsequent posts, 
      setting initial page number values. 
     """ 
     data = xml.xpath("//*[@id='MainContent_physicianSearchView_gvResults_ddlPager']/@name") 
     self.pge = data[0] 
     self.ev = data[1] 
     self.post_data2["__EVENTTARGET"] = self.ev 
     self.post_data2[self.ev] = "1" 
     self.post_data2[self.pge] = "2" 

    @staticmethod 
    def put_validation(xml, d): 
     """Need to request new __EVENTVALIDATION for each post. 
     """ 
     ev = xml.xpath("//*[@id='__EVENTVALIDATION']/@value")[0] 
     vs = xml.xpath("//*[@id='__VIEWSTATE']/@value")[0] 
     d["__EVENTVALIDATION"] = ev 
     d["__VIEWSTATE"] = vs 

    def next_page(self, d): 
     """Increments the page number by one per iteration.""" 
     e = self.post_data2[self.ev] 
     v = self.post_data2[self.pge] 
     self.post_data2[self.pge] = str(int(v) + 1) 
     self.post_data2[self.ev] = str(int(e) + 1) 

    def start(self): 
     with requests.session() as s: 
      # get initial page to pull __EVENTVALIDATION etc.. 
      req = s.get(self.url, headers={"user-agent": self.user_agent}).content 
      # add __EVENTVALIDATION" to post data. 
      self.put_validation(fromstring(req), self.post_data1) 

      xml = fromstring(req) 
      # populate the rest of the post data. 
      self.populate(xml) 
      resp = fromstring(s.post(self.url, data=self.post_data1, headers=self.post_header).content) 
      # yield first page results. 
      yield resp 
      # fill post data for next pages. 
      self.populate2(resp) 
      # when this is an empty list, we will have hit the last page. 
      nxt = xml.xpath("//*[@id='MainContent_physicianSearchView_gvResults_btnNextPage']/@disabled") 
      while not nxt: 
       # update __EVENTVALIDATION token and _VIEWSTATE. 
       self.put_validation(fromstring(s.get(self.url).content), self.post_data2) 

       # post to get next page of results. 
       yield fromstring(s.post(url, data=self.post_data2, headers=self.post_header).content) 

       nxt = xml.xpath("//*[@id='MainContent_physicianSearchView_gvResults_btnNextPage']/@disabled") 
       self.next_page(nxt_d) 


ua = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36" 
url = "http://search.cpsa.ca/PhysicianSearch" 
c = Crawler(ua, url) 
for tree in c.start(): 
    # use tree 
+2

Chương trình này lái xe đưa bạn về tinh thần , Vì vậy, -1 –

+1

@BhargavRao, lol nó sẽ tồi tệ hơn nếu nó đánh bại tôi;) –

+0

Tôi cũng đã mất trí. Cảm ơn một tấn cho sự giúp đỡ - xác minh bây giờ rằng công trình này. Tôi thực sự đã tìm ra cách giải quyết một phút trước khi bạn bỏ qua nó, lol. –

Các vấn đề liên quan