Scrape ResearchGate 모든 기관 구성원을 파이썬으로

27119 단어
  • What will be scraped
  • Prerequisites

  • Full Code
  • Extracting data from the JSON string

  • Links



  • 스크랩 할 것





    전제 조건



    CSS 선택자를 사용한 기본 지식 스크래핑

    CSS 선택기는 스타일이 적용되는 마크업 부분을 선언하므로 일치하는 태그 및 속성에서 데이터를 추출할 수 있습니다.

    CSS 선택기로 스크랩하지 않은 경우 그것이 무엇인지, 장단점, 웹 스크래핑 관점에서 왜 중요한지, 그리고 가장 일반적인 접근 방식을 보여주는 전용 블로그 게시물how to use CSS selectors when web-scraping이 있습니다. 웹 스크래핑 시 CSS 선택기를 사용합니다.

    별도의 가상 환경

    요컨대, 동일한 시스템에서 서로 공존할 수 있는 서로 다른 Python 버전을 포함하여 설치된 라이브러리의 독립 세트를 생성하여 라이브러리 또는 Python 버전 충돌을 방지하는 것입니다.

    이전에 가상 환경으로 작업한 적이 없다면 내 전용 블로그 게시물Python virtual environments tutorial using Virtualenv and Poetry을 살펴보고 익숙해지십시오.

    📌참고: 이것은 이 블로그 게시물에 대한 엄격한 요구 사항이 아닙니다.

    라이브러리 설치:

    pip install parsel playwright
    


    차단될 확률 감소

    요청이 차단될 가능성이 있습니다. how to reduce the chance of being blocked while web-scraping을 살펴보십시오. 대부분의 웹사이트에서 차단을 우회하는 11가지 방법이 있습니다.

    전체 코드




    from parsel import Selector
    from playwright.sync_api import sync_playwright
    import re, json, time
    
    
    def scrape_institution_members(institution: str):
        with sync_playwright() as p:
    
            institution_memebers = []
            page_num = 1 
    
            members_is_present = True
            while members_is_present:
    
                browser = p.chromium.launch(headless=True, slow_mo=50)
                page = browser.new_page()
                page.goto(f"https://www.researchgate.net/institution/{institution}/members/{page_num}")
                selector = Selector(text=page.content())
    
                print(f"page number: {page_num}")
    
                for member in selector.css(".nova-legacy-v-person-list-item"):
                    name = member.css(".nova-legacy-v-person-list-item__align-content a::text").get()
                    link = f'https://www.researchgate.net{member.css(".nova-legacy-v-person-list-item__align-content a::attr(href)").get()}'
                    profile_photo = member.css(".nova-legacy-l-flex__item img::attr(src)").get()
                    department = member.css(".nova-legacy-v-person-list-item__stack-item:nth-child(2) span::text").get()
                    desciplines = member.css("span .nova-legacy-e-link::text").getall()
    
                    institution_memebers.append({
                        "name": name,
                        "link": link,
                        "profile_photo": profile_photo,
                        "department": department,
                        "descipline": desciplines
                    })
    
                # check for Page not found selector
                if selector.css(".headline::text").get():
                    members_is_present = False
                else:
                    time.sleep(2) # use proxies and captcha solver instead of this
                    page_num += 1 # increment a one. Pagination
    
            print(json.dumps(institution_memebers, indent=2, ensure_ascii=False))
            print(len(institution_memebers)) # 624 from a EM-Normandie-Business-School
    
            browser.close()
    
            """
            you can also render the page and extract data from the inline JSON string,
            however, it's messy and from my perspective, it is easier to scrape the page directly.
            """
    
            # https://regex101.com/r/8qjfnH/1
            # extracted_data = re.findall(r"\s+RGCommons\.react\.mountWidgetTree\(({\"data\":{\"menu\".*:true})\);;",
            #                        str(page.content()))[0]
            # json_data = json.loads(extracted_data)
            # print(json_data)
    
    scrape_institution_members(institution="EM-Normandie-Business-School")
    


    코드 설명



    라이브러리 가져오기:

    from parsel import Selector
    from playwright.sync_api import sync_playwright
    import re, json, time
    



    암호
    설명


    parsel
    HTML/XML 문서를 구문 분석합니다. XPath를 지원합니다.

    playwright
    브라우저 인스턴스로 페이지를 렌더링합니다.
    re데이터의 일부를 정규식과 일치시킵니다.
    jsonPython 사전을 JSON 문자열로 변환합니다.
    time요청 블록을 우회하는 실용적인 방법이 아닙니다. 대신 프록시/캡차 솔버를 사용하십시오.


    함수를 정의합니다.

    def scrape_institution_members(institution: str):
        # ...
    



    암호
    설명

    institution: str파이썬에게 institutionstr 가 되어야 한다고 알려줍니다.


    context manager이 있는 playwright 열기:

    with sync_playwright() as p:
        # ...
    


    브라우저 인스턴스를 실행하고 페이지를 열고goto HTML/XML 파서에 대한 응답을 전달합니다.

    browser = p.chromium.launch(headless=True, slow_mo=50)
    page = browser.new_page()
    page.goto(f"https://www.researchgate.net/institution/{institution}/members/{page_num}")
    selector = Selector(text=page.content())
    



    암호
    설명


    p.chromium.launch()
    Chromium 브라우저 인스턴스를 시작합니다.

    headless
    기본 값이더라도 헤드리스 모드에서 실행하도록 명시적으로 지시합니다playwright.

    slow_mo
    실행 속도를 늦추라고 지시합니다playwright.

    browser.new_page()
    새 페이지를 엽니다.


    임시 목록을 추가하고, 페이지 번호를 설정하고, 루프 동안 루프를 종료하고 루프를 종료하기 위한 예외를 확인합니다.

    institution_memebers = []
    page_num = 1
    
    members_is_present = True
    while members_is_present:
    
          # extraction code
    
          # check for Page not found selector
          if selector.css(".headline::text").get():
              members_is_present = False
          else:
              time.sleep(2) # use proxies and captcha solver instead of this
              page_num += 1 # increment a one. Pagination
    


    각 페이지에서 구성원 결과를 반복하고 데이터를 추출하고append 임시list로:

    for member in selector.css(".nova-legacy-v-person-list-item"):
        name = member.css(".nova-legacy-v-person-list-item__align-content a::text").get()
        link = f'https://www.researchgate.net{member.css(".nova-legacy-v-person-list-item__align-content a::attr(href)").get()}'
        profile_photo = member.css(".nova-legacy-l-flex__item img::attr(src)").get()
        department = member.css(".nova-legacy-v-person-list-item__stack-item:nth-child(2) span::text").get()
        desciplines = member.css("span .nova-legacy-e-link::text").getall()
    
        institution_memebers.append({
            "name": name,
            "link": link,
            "profile_photo": profile_photo,
            "department": department,
            "descipline": desciplines
        })
    



    암호
    설명

    css()
    to parse data from the passed CSS selector(s) . 후드 아래의 모든 CSS query traslates to XPath using csselect package.
    ::text/::attr(attribute)
    to extract textual or attribute data 노드에서.
    get()/getall()
    to get actual data from a matched node , 또는 get a list of matched data from nodes .


    추출된 데이터, 추출된 구성원의 lengthclose 브라우저 인스턴스 인쇄:

    print(json.dumps(institution_memebers, indent=2, ensure_ascii=False))
    print(len(institution_memebers)) # 624 from a EM-Normandie-Business-School
    
    browser.close()
    


    JSON 출력의 일부(첫 번째 결과는 첫 번째 멤버이고 마지막은 최신 멤버임):

    [
      {
        "name": "Sylvaine Castellano",
        "link": "https://www.researchgate.netprofile/Sylvaine-Castellano",
        "profile_photo": "https://i1.rgstatic.net/ii/profile.image/341867548954625-1458518983237_Q64/Sylvaine-Castellano.jpg",
        "department": "EM Normandie Business School",
        "descipline": [
          "Sustainable Development",
          "Sustainability",
          "Innovation"
        ]
      }, ... other results
      {
        "name": "Constance Biron",
        "link": "https://www.researchgate.netprofile/Constance-Biron-3",
        "profile_photo": "https://c5.rgstatic.net/m/4671872220764/images/template/default/profile/profile_default_m.jpg",
        "department": "Marketing",
        "descipline": []
      }
    ]
    


    JSON 문자열에서 데이터 추출



    doctype을 포함하여 페이지의 전체 HTML 콘텐츠를 가져올 페이지 parsel 데이터를 인쇄하고 정규식을 사용하여 데이터를 구문 분석하여 content()를 사용하지 않고 데이터를 스크랩할 수 있습니다.

    페이지를 직접 구문 분석하는 것보다 이 접근 방식을 선호하는 경우를 대비하여 이 옵션도 표시하고 있습니다.

    # https://regex101.com/r/8qjfnH/1
    extracted_data = re.findall(r"\s+RGCommons\.react\.mountWidgetTree\(({\"data\":{\"menu\".*:true})\);;",
                           str(page.content()))[0]
    json_data = json.loads(extracted_data)
    print(json_data)
    


    출력:



    데이터를 추출하는 것이 JSON 문자열에서 실질적으로 더 설득력 있는 것처럼 보이지만 fullName 키에 액세스하는 예를 살펴보겠습니다.

                                         👇👇👇👇👇
    initState.rigel.store.account(id:\\\"AC:2176142\\\").fullName
    


    이렇게 하면 두 가지 추가 단계가 있습니다. 사용자 ID를 찾고 비교하여 ID가 ​​사용자 ID와 일치하는지 확인합니다.


    연결


  • GitHub file in the repository



  • 가입 |

    Feature Request 💫 또는 Bug 🐞 추가

    🌼

    좋은 웹페이지 즐겨찾기