แยกวิเคราะห์ชื่อตั้งแต่หน้าที่ 3 เป็นต้นไปไม่ได้

ฉันได้สร้างสคริปต์ใน python โดยใช้โมดูลคำขอและไลบรารี BeautifulSoup เพื่อดึงชื่อของสมาชิกรายต่างๆ จากเว็บไซต์ สคริปต์สามารถดึงชื่อจากหน้าแรกและหน้าสองได้อย่างไม่มีที่ติ อย่างไรก็ตาม จะขูดชื่อเดียวกันตั้งแต่หน้าสามเป็นต้นไป สิ่งที่ฉันสังเกตได้ว่าตรรกะของหน้าถัดไปอยู่ภายในค่า __EVENTTARGET เช่นใน dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl07 และ dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl09 เป็นต้น สคริปต์สามารถเพิ่มตัวเลขได้ตามนั้น แต่ผลลัพธ์หลังจากหน้าสองยังคงเหมือนเดิม

หากต้องการเติมข้อมูลผลลัพธ์จาก เว็บไซต์ สิ่งที่คุณต้องทำคือคลิกที่ปุ่มค้นหาโดยไม่ต้องเปลี่ยนแปลงอะไรเลย จากนั้นคุณสามารถคลิกที่หน้า 2,3,4 ฯลฯ เพื่อไปยังหน้าที่เกี่ยวข้องได้

ฉันได้ลองแล้ว (คัดลอกข้อมูลจากสองหน้าแรก):

import requests
from bs4 import BeautifulSoup

link = 'https://www.icsi.in/student/Members/MemberSearch.aspx?SkinSrc=%5BG%5DSkins/IcsiTheme/IcsiIn-Bare&ContainerSrc=%5BG%5DContainers/IcsiTheme/NoContainer'

with requests.Session() as s:
    s.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
    r = s.get(link)
    soup = BeautifulSoup(r.text,"lxml") 
    payload = {i['name']:i.get('value','') for i in soup.select('input[name]')}
    payload['__EVENTTARGET'] = 'dnn$ctr410$MemberSearch$btnSearch'

    page = 5
    while True:
        r = s.post(link,data=payload)
        soup = BeautifulSoup(r.text,"lxml")
        for item in soup.select("span[id$='_lblFullName']"):
            print(item.text)

        page+=2
        payload = {i['name']:i.get('value','') for i in soup.select('input[name]')}
        if len(str(page))==1:
            payload['__EVENTTARGET'] = 'dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl0{}'.format(page)
        else:
            payload['__EVENTTARGET'] = 'dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl{}'.format(page)

        payload['__dnnVariable'] = {'__scdoff':'1','__dnn_pageload':'__dnn_setScrollTop();'}
        payload['ScrollTop'] = '400'

ฉันจะดึงชื่อจากหน้าที่เหลือหลังจากหน้าที่ 2 ได้อย่างไร


comment
ดูเหมือนว่าหน้า Like ASP.NET - มันสามารถส่งค่า POST ได้มากมาย - ไม่ใช่แค่ __EVENTTARGET - และคุณอาจต้องส่งทั้งหมด - เช่นเดียวกับคำขอ POST FIRTS: ใช้ DevTools ใน Firefox/Chrome เพื่อดูคำขอทั้งหมดที่ส่งจากเบราว์เซอร์เมื่อคุณไปที่หน้าถัดไป - และดูว่าคำขอส่งค่าใดบ้าง และเป็นคำขอ GET หรือ POST รหัสของคุณต้องส่งเหมือนกัน   -  person furas    schedule 11.05.2020
comment
ผมส่งน่าจะทั้งหมดนะครับ. หากคุณพิมพ์เพย์โหลด คุณจะเห็นพารามิเตอร์ที่จำเป็นอยู่ในนั้น ขอบคุณ.   -  person MITHU    schedule 11.05.2020
comment
BTW คุณสามารถใช้ "{:02}".format(7) เพื่อรับ 07 แทน 7 จากนั้นคุณไม่จำเป็นต้องตรวจสอบ if len(str(page))==1: (BTW: แทน if len(str(page))==1: คุณสามารถตรวจสอบ if page < 10: ได้เลย)   -  person furas    schedule 11.05.2020
comment
เมื่อฉันตรวจสอบ payload.keys() และเปรียบเทียบกับคีย์ที่ส่งในเว็บเบราว์เซอร์ ฉันจะเห็นคีย์ที่เบราว์เซอร์ไม่ส่ง - กล่าวคือ ปุ่มสำหรับปุ่มที่มีลูกศร (เพื่อเลื่อนไปยังหน้าแรก/หน้าสุดท้าย/ก่อนหน้า/หน้าถัดไป) และอาจทำให้เกิดปัญหาได้ เช่น. dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl02 สำหรับปุ่มที่ย้ายไปหน้าแรก   -  person furas    schedule 11.05.2020
comment
ฉันเพิ่มบรรทัดนี้ payload.pop('dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl02') ที่ด้านล่างของลูป while เพื่อเตะคีย์ออกจากเพย์โหลด แต่ดูเหมือนว่าจะไม่สามารถแก้ไขปัญหาได้ ขอบคุณ.   -  person MITHU    schedule 11.05.2020
comment
แต่อาจมีชื่ออื่นๆ ได้อีกมากมาย เนื่องจากมีปุ่มอื่นๆ เช่น ฉันเห็น ...ctl28 สำหรับลูกศรไปยังหน้าถัดไป (แต่ฉันไม่แน่ใจว่าจะไม่ใช้ชื่ออื่นหรือไม่หากมีหน้าให้แสดงน้อย) ฉันไม่แน่ใจ แต่หน้าเว็บยังสามารถใช้ JavaScript เพื่อคำนวณ __EVENTVALIDATION และเซิร์ฟเวอร์สามารถตรวจสอบได้ - แต่ requests/bs4 ไม่สามารถเรียกใช้ JavaScript ได้   -  person furas    schedule 11.05.2020
comment
@MITHU ตรวจสอบคำตอบด้านล่าง   -  person αԋɱҽԃ αмєяιcαη    schedule 11.05.2020


คำตอบ (2)


มันจะเริ่มทำงานหากฉันลบคีย์น้ำหนักบรรทุกเช่น dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl02 ซึ่งเป็นปุ่มสำหรับปุ่มลูกศรออกจากคีย์

    name_length = len('dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl02')

    for key in list(payload.keys()):
        if key.startswith('dnn') and len(key) == name_length:
            payload.pop(key)
            print(key)

แต่คุณสามารถใช้วิธีการจาก αԋɱҽԃ αмєяιcαη คำตอบเพื่อให้แน่ใจว่าคุณส่งเฉพาะค่าที่จำเป็นเท่านั้น


import requests
from bs4 import BeautifulSoup

link = 'https://www.icsi.in/student/Members/MemberSearch.aspx?SkinSrc=%5BG%5DSkins/IcsiTheme/IcsiIn-Bare&ContainerSrc=%5BG%5DContainers/IcsiTheme/NoContainer'

with requests.Session() as s:
    s.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
    r = s.get(link)
    soup = BeautifulSoup(r.text,"lxml")

    payload = {i['name']:i.get('value','') for i in soup.select('input[name]')}
    payload['__EVENTTARGET'] = 'dnn$ctr410$MemberSearch$btnSearch'

    page = 5
    while True:

        r = s.post(link, data=payload)
        soup = BeautifulSoup(r.text, "lxml")
        for item in soup.select("span[id$='_lblFullName']"):
            print(item.text)

        page += 2

        payload = {i['name']:i.get('value','') for i in soup.select('input[name]')}
        payload['__EVENTTARGET'] = 'dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl{:02}'.format(page)

        name_length = len('dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl02')
        for key in list(payload.keys()):
            if key.startswith('dnn') and len(key) == name_length:
                payload.pop(key)
                print(key)

        payload['__dnnVariable'] = {'__scdoff':'1','__dnn_pageload':'__dnn_setScrollTop();'}
        payload['ScrollTop'] = '400'

แก้ไข: หน้าใช้ระบบที่ซับซ้อนมากขึ้นและหลังจาก 10 หน้าก็แสดงลิงก์ใหม่แต่มีค่าเดิม ctl07, ctl09 แทนที่จะใช้ลิงก์นี้ ผมใช้ชื่อจากปุ่มที่มีลูกศรไปยังหน้าถัดไป - ตอนเริ่มต้นจะมีค่า ctrl28 แต่หลังจาก 10 หน้าก็มี ctrl30 (เนื่องจากมีลิงก์มากกว่า - จึงเพิ่มลิงก์ ... ไปยังรายการถัดไป/ก่อนหน้าของ 10 หน้า)

import requests
from bs4 import BeautifulSoup

link = 'https://www.icsi.in/student/Members/MemberSearch.aspx?SkinSrc=%5BG%5DSkins/IcsiTheme/IcsiIn-Bare&ContainerSrc=%5BG%5DContainers/IcsiTheme/NoContainer'

with requests.Session() as s:
    s.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
    r = s.get(link)
    soup = BeautifulSoup(r.text,"lxml")

    payload = {i['name']:i.get('value','') for i in soup.select('input[name]')}
    payload['__EVENTTARGET'] = 'dnn$ctr410$MemberSearch$btnSearch'

    page = 1  # I don't need it to generate lins, now I use it only to display page number
    while True:
        print('page:', page)
        page += 1

        r = s.post(link, data=payload)
        soup = BeautifulSoup(r.text, "lxml")
        for item in soup.select("span[id$='_lblFullName']"):
            print(item.text)

        payload = {i['name']:i.get('value','') for i in soup.select('input[name]')}

        name_length = len('dnn$ctr410$MemberSearch$grdMembers$ctl00$ctl02$ctl01$ctl28')
        for key in list(payload.keys()):
            if key.startswith('dnn') and len(key) == name_length:
                payload.pop(key)
                #print(key)

        # button with arrow to next page

        next_page = soup.select("input[class='rgPageNext']")
        if not next_page:
            break

        next_page = next_page[0]['name']
        print(next_page)
        payload[next_page] = ''

        payload['__dnnVariable'] = {'__scdoff':'1','__dnn_pageload':'__dnn_setScrollTop();'}
        payload['ScrollTop'] = '400'
person furas    schedule 11.05.2020
comment
หลังจากผ่านไป 12 หรือ 13 หน้า สคริปต์จะเริ่มสร้างผลลัพธ์ของหน้าก่อนหน้าซ้ำๆ - person MITHU; 11.05.2020
comment
คุณตรวจสอบในเว็บเบราว์เซอร์หรือไม่? อาจใช้ค่าที่แตกต่างจากที่คุณคาดหวัง ตอนนี้ฉันได้ตรวจสอบลิงก์ไปยังหน้า 11 แล้ว และมันใช้อีกครั้ง ctl07 แทนที่จะเป็น ctl25 ดังนั้นจึงทำงานแตกต่างไปจากที่เราคาดไว้ - person furas; 11.05.2020
comment
แทนที่จะสร้างค่าสำหรับ '__EVENTTARGET' ฉันใช้ชื่อจาก select("input[class='rgPageNext']")[0]['name'] (ปุ่มที่มีลูกศรไปยังหน้าถัดไป) จากนั้นฉันก็ได้ค่าที่ถูกต้องแม้จะผ่านไป 10 หน้าแล้วก็ตาม - person furas; 11.05.2020

จริงๆ แล้วคุณจำเป็นต้องรวมพารามิเตอร์โพสต์แบบเต็ม Payload

เราต้องทำสิ่งนั้นภายใน Session เดียวกันโดยใช้ requests.Session() เนื่องจากการแบ่งหน้าเว็บไซต์ใช้การหมุน function ตาม __dnnVariable ซึ่งเซิร์ฟเวอร์ได้รับภายใต้คำขอ JS ซึ่งแปลเป็น Loop

ความหมายจริงๆ อยู่ที่ไหน Next

ดังนั้นฉันจึงได้ส่งคำขอ GET ก่อนและดึงข้อมูลที่จำเป็น params (บางส่วนเป็นแบบไดนามิกและบางส่วนเป็นแบบคงที่)

จากนั้นฉันก็ขอโพสต์ภายใต้ session เดียวกัน

import requests
import re
from bs4 import BeautifulSoup
from urllib.parse import unquote

data = {
    '__EVENTTARGET': "dnn$ctr410$MemberSearch$btnSearch",
    '__EVENTARGUMENT': '',
    '__VIEWSTATEENCRYPTED': '',
    'dnn$ctlHeader$dnnSearch$Search': 'SiteRadioButton',
    'dnn$ctlHeader$dnnSearch$txtSearch': '',
    'dnn$ctr410$MemberSearch$txtFirstName': '',
    'dnn$ctr410$MemberSearch$txtLastName': '',
    'dnn$ctr410$MemberSearch$ddlMemberType': 0,
    'dnn$ctr410$MemberSearch$txtMembershipNumber': '',
    'dnn$ctr410$MemberSearch$txtCpNumber': '',
    'dnn$ctr410$MemberSearch$txtCity': '',
    'dnn$ctr410$MemberSearch$txtOrganisation': '',
    'dnn$ctr410$MemberSearch$txtAddress2': '',
    'dnn$ctr410$MemberSearch$txtAddress3': '',
    'dnn$ctr410$MemberSearch$txtEmail': '',
    'dnn_ctr410_MemberSearch_grdMembers_ClientState': '',
    'ScrollTop': 432,
    '__dnnVariable': '{"__scdoff":"1","__dnn_pageload":"__dnn_setScrollTop();"}'
}


def main(url):
    with requests.Session() as req:
        r = req.get(url)
        soup = BeautifulSoup(r.content, 'html.parser')
        data['StylesheetManager_TSSM'] = re.search(
            r"hf.value \+= '(.*?)\'", r.text).group(1)
        data['ScriptManager_TSM'] = unquote(soup.findAll('script', src=True)
                                            [2]['src']).split("=", 3)[-1]
        data['__VIEWSTATE'] = soup.find("input", id="__VIEWSTATE").get("value")
        data['__VIEWSTATEGENERATOR'] = soup.find(
            "input", id="__VIEWSTATEGENERATOR").get("value")
        data['__EVENTVALIDATION'] = soup.find(
            "input", id="__EVENTVALIDATION").get("value")

        for _ in range(10):
            r = req.post(url, data=data)
            soup = BeautifulSoup(r.content, 'html.parser')
            names = [name.text for name in soup.select("div.name_head")]
            page = soup.select_one(
                "a.rgCurrentPage").next_sibling['href'].split("'")[1]
            data['__EVENTTARGET'] = page
            data['__EVENTVALIDATION'] = soup.find(
                "input", id="__EVENTVALIDATION").get("value")
            data['__VIEWSTATE'] = soup.find(
                "input", id="__VIEWSTATE").get("value")
            print(names)


main("https://www.icsi.in/student/Members/MemberSearch.aspx")

หมายเหตุ: ในแต่ละคำขอ คุณจะได้รับข้อมูลไม่เรียงลำดับเนื่องจากฟังก์ชัน search ใช้ random บนเซิร์ฟเวอร์ส่วนหลัง

เอาท์พุท:

['SH. DILIP RAGHUNATH KOTWAL', 'SH. ARUNODAY ROY MUKHERJEE', 'SH. J SUBRAMANI', 'SH. R KRISHNAMANI', 'SH. R NARAYANASWAMI', 'SH. M V GOPALAKRISHNAN', 'SH. RAJAM KRISHNAMURTHY', 'SH. V SIVASUBRAMANIAN', 'SH. V RAGHAVENDRAN', 'SH. G V AIMAN']
['SH. K J MATHEW', 'SH. K K GHOSH', 'SH. SUBHASH CHANDER DHAWAN', 'SH. BABU RAM MAHESWARI', 'SH. S SWAMINATHAN', 'SH. T S A AIYER', 'SH. KOVILOOR VIJAYARAGHAVACHARI SAMPATHKUMAR', 'SH. M KRISHNAN', 'SH. R N BANSAL', 'SH. N V RAMAN']
['SH. R VENKATARAMANI', 'SH. UTPALENDU ROY CHOUDHURY', 'SH. LAKSHMI NARAYANAN V', 'SH. PARIJAT KUMAR HORE', 'SH. B R VENKATESAN', 'SH. KISHAN GOPAL SOMANI', 'SH. O P GANERIWALA', 'SH. P T KUPPUSWAMY', 'SH. U P MATHUR', 'SH. N N UPADHYAY']
['SH. N K BHANDARI', 'SH. S R C SETTY', 'SH. S V BALASUBRAMANIAN', 'SH. HOSHIE HIRJI MALGHAM', 'SH. KAIKOBAD SORABJI ITALIA', 'SH. K SIVADAS', 'SH. K K SIVARAMAKRISHNAN', 'SH. A CHANDRASEKARAN', 'SH. R PONNAMBALAM', 'SH. T K B VENKATARAMAN']
['SH. NARINDER PAL', 'SH. PARKASH ATAM', 'SH. K A PARTHASARATHY', 'SH. SURESH CHANDRA OSWAL', 'SH. MAHENDRA KANTILAL SHAH', 'SH. V. SANTHANAKRISHNA', 'SH. VASANT NARAYAN GOGATE', 'SH. MANEKLAL 
PATEL', 'SH. B N VISHWANATH', 'SH. B S L NARAYAN']
['SH. P L N VIJAYANAGAR', 'SH. SHREEPAD MARTAND  KORDE', 'SH. SHIV BHAGWAN KOTHARI', 'SH. R B POPLAI', 'SH. RAMESH KHANNA', 'SH. RAVINDER NATH JOSHI', 'SH. VIDYA SAGAR AGGARWAL', 'SH. ARVIND JAYKUMAR CHAKOTE', 'SH. V RAMASESHAN', 'SH. BADRINARAYAN BALDAWA']
['SH. C GOVINDANKUTTY', 'SH. A G MADHAVAN', 'SH. DHIRAJ NATH BHATTACHARYYA', 'SH. RAMESHWAR LAL INANI', 'SH. RAMESHWARDAS C DAGA', 'SH. R SUBRAMANIAN', 'SH. S M REGE', 'SH. NARENDRA KUMAR KAPOOR', 'SH. K RAMAMURTHI', 'SH. ROOPENDRA NARAYAN ROY']
['SH. KALYAN KUMAR MITRA', 'SH. KALYANASUNDARAM ', 'SH. N A SESHADRI', 'SH. RAJENDRA KUMAR JAIN', 'SH. BISWAJIT SEN', 'SH. RAMKRISHNA NATHOOMAL  AGRAWAL', 'SH. P C SHETH', 'SH. K S NATARAJAN', 
'SH. S N DAMLE', 'SH. A M FADIA']
['DR. K N M RAO', 'SH. IYER M. RAMASWAMY', 'SH. DILIP KANTI MAZUMDAR', 'SH. RAM CHANDRA NIGAM', 'SH. SUBRAHMANIAM VISWANATHAN', 'SH. SURESH KUMAR JERATH', 'SH. A Y SRINIVASAN', 'DR. S C GARG', 
'SH. CHANDRA PRAKASH SHARDA', 'SH. M P JAIN']
['SH. E S DWARKANATH', 'SH. MYSORE SHAMANNA  RAMACHANDRA', 'SH. SUBHASH CHANDER SINGHAL', 'SH. T T SINHA', 'SH. G R BHANDARI', 'SH. M P GOEL', 'SH. CHOKKANATHAPURAM SUBRAMANIAN  NATESAN', 'SH. 
V M PATEL', 'SH. BIJOY KUMAR AGARWALLA', 'SH. BAHADUR CHAND JAIN']
person αԋɱҽԃ αмєяιcαη    schedule 11.05.2020
comment
ใช่ โซลูชันของคุณทั้งสองใช้งานได้ ขอบคุณล้านล้าน - person MITHU; 11.05.2020
comment
@MITHU ยินดีต้อนรับ อย่าลังเลที่จะยอมรับคำตอบของฉันโดยทำเครื่องหมายถูกข้างคำตอบถ้ามันช่วยคุณได้ คุณสามารถโหวตได้หากคุณชอบเช่นกัน - person αԋɱҽԃ αмєяιcαη; 11.05.2020