Не могу перейти на следующую страницу, используя запрос - PullRequest
0 голосов
/ 29 июня 2018

Я написал скрипт на python для получения разных ссылок, ведущих на разные статьи с веб-страницы. После запуска моего сценария я могу получить их без нареканий. Однако проблема, с которой я сталкиваюсь, заключается в том, что ссылки на статьи пересекают несколько страниц, поскольку они имеют большие числа, чтобы поместиться на одной странице. если я нажму на кнопку следующей страницы, прикрепленную информацию я смогу увидеть в инструментах разработчика, которые на самом деле производят ajax-вызов посредством пост-запроса. Поскольку к этой кнопке на следующей странице нет ссылок, я не могу найти способ перейти на следующую страницу и проанализировать ссылки оттуда. Я пробовал с post request с этим formdata, но, похоже, он не работает. Куда я иду не так?

Ссылка на целевую страницу, содержащую статьи

Это информация, которую я получаю, используя инструменты разработчика Chrome, когда нажимаю кнопку следующей страницы:

GENERAL
=======================================================
Request URL: https://www.ncbi.nlm.nih.gov/pubmed/
Request Method: POST
Status Code: 200 OK
Remote Address: 130.14.29.110:443
Referrer Policy: origin-when-cross-origin

RESPONSE HEADERS
=======================================================
Cache-Control: private
Connection: Keep-Alive
Content-Encoding: gzip
Content-Security-Policy: upgrade-insecure-requests
Content-Type: text/html; charset=UTF-8
Date: Fri, 29 Jun 2018 10:27:42 GMT
Keep-Alive: timeout=1, max=9
NCBI-PHID: 396E3400B36089610000000000C6005E.m_12.03.m_8
NCBI-SID: CE8C479DB3510951_0083SID
Referrer-Policy: origin-when-cross-origin
Server: Apache
Set-Cookie: ncbi_sid=CE8C479DB3510951_0083SID; domain=.nih.gov; path=/; expires=Sat, 29 Jun 2019 10:27:42 GMT
Set-Cookie: WebEnv=1Jqk9ZOlyZSMGjHikFxNDsJ_ObuK0OxHkidgMrx8vWy2g9zqu8wopb8_D9qXGsLJQ9mdylAaDMA_T-tvHJ40Sq_FODOo33__T-tAH%40CE8C479DB3510951_0083SID; domain=.nlm.nih.gov; path=/; expires=Fri, 29 Jun 2018 18:27:42 GMT
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-UA-Compatible: IE=Edge
X-XSS-Protection: 1; mode=block

REQUEST HEADERS
========================================================
Accept: text/html, */*; q=0.01
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 395
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: ncbi_sid=CE8C479DB3510951_0083SID; _ga=GA1.2.1222765292.1530204312; _gid=GA1.2.739858891.1530204312; _gat=1; WebEnv=18Kcapkr72VVldfGaODQIbB2bzuU50uUwU7wrUi-x-bNDgwH73vW0M9dVXA_JOyukBSscTE8Qmd1BmLAi2nDUz7DRBZpKj1wuA_QB%40CE8C479DB3510951_0083SID; starnext=MYGwlsDWB2CmAeAXAXAbgA4CdYDcDOsAhpsABZoCu0IA9oQCZxLJA===
Host: www.ncbi.nlm.nih.gov
NCBI-PHID: 396E3400B36089610000000000C6005E.m_12.03
Origin: https://www.ncbi.nlm.nih.gov
Referer: https://www.ncbi.nlm.nih.gov/pubmed
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
X-Requested-With: XMLHttpRequest

FORM DATA
========================================================
p$l: AjaxServer
portlets: id=relevancesortad:sort=;id=timelinead:blobid=NCID_1_120519284_130.14.22.215_9001_1530267709_1070655576_0MetA0_S_MegaStore_F_1:yr=:term=%222015%22%5BDate%20-%20Publication%5D%20%3A%20%223000%22%5BDate%20-%20Publication%5D;id=reldata:db=pubmed:querykey=1;id=searchdetails;id=recentactivity
load: yes

Пока это мой сценарий (запрос get работает без ошибок, если он не закомментирован, но для первой страницы):

import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

geturl = "https://www.ncbi.nlm.nih.gov/pubmed/?term=%222015%22%5BDate+-+Publication%5D+%3A+%223000%22%5BDate+-+Publication%5D"
posturl = "https://www.ncbi.nlm.nih.gov/pubmed/"

# res = requests.get(geturl,headers={"User-Agent":"Mozilla/5.0"})
# soup = BeautifulSoup(res.text,"lxml")
# for items in soup.select("div.rslt p.title a"):
#     print(items.get("href"))

FormData={
    'p$l': 'AjaxServer',
    'portlets': 'id=relevancesortad:sort=;id=timelinead:blobid=NCID_1_120519284_130.14.22.215_9001_1530267709_1070655576_0MetA0_S_MegaStore_F_1:yr=:term=%222015%22%5BDate%20-%20Publication%5D%20%3A%20%223000%22%5BDate%20-%20Publication%5D;id=reldata:db=pubmed:querykey=1;id=searchdetails;id=recentactivity',
    'load': 'yes'
    }

req = requests.post(posturl,data=FormData,headers={"User-Agent":"Mozilla/5.0"})
soup = BeautifulSoup(req.text,"lxml")
for items in soup.select("div.rslt p.title a"):
    print(items.get("href"))

Кстати, URL в браузере становится "https://www.ncbi.nlm.nih.gov/pubmed", когда я нажимаю ссылку на следующую страницу.

Я не хочу идти на какое-либо решение, связанное с любым браузерным симулятором. Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 07 июля 2018

Не относиться к этому вопросу как к проблеме XY, так как, если она решена, она должна стать очень интересным решением, НО я нашел решение для этой специфической проблемы, которая гораздо более эффективна: использование NCBI Entrez Programming Utilities и удобный, с открытым исходным кодом, неофициальный репозиторий Entrez .

С помощью скрипта entrez.py из репозитория Entrez в моем PATH я создал этот скрипт, который распечатывает ссылки так, как вы хотите:

from entrez import on_search
import re

db = 'pubmed'
term = '"2015"[Date - Publication] : "3000"[Date - Publication]'
link_base = f'https://www.ncbi.nlm.nih.gov/{db}/'

def links_generator(db, term):
    for line in on_search(db=db, term=term, tool='link'):
        match = re.search(r'<Id>([0-9]+)</Id>', line)
        if match: yield (link_base + match.group(1))

for link in links_generator(db, term):
    print(link)

Выход:

https://www.ncbi.nlm.nih.gov/pubmed/29980165
https://www.ncbi.nlm.nih.gov/pubmed/29980164
https://www.ncbi.nlm.nih.gov/pubmed/29980163
https://www.ncbi.nlm.nih.gov/pubmed/29980162
https://www.ncbi.nlm.nih.gov/pubmed/29980161
https://www.ncbi.nlm.nih.gov/pubmed/29980160
https://www.ncbi.nlm.nih.gov/pubmed/29980159
https://www.ncbi.nlm.nih.gov/pubmed/29980158
https://www.ncbi.nlm.nih.gov/pubmed/29980157
https://www.ncbi.nlm.nih.gov/pubmed/29980156
https://www.ncbi.nlm.nih.gov/pubmed/29980155
https://www.ncbi.nlm.nih.gov/pubmed/29980154
https://www.ncbi.nlm.nih.gov/pubmed/29980153
https://www.ncbi.nlm.nih.gov/pubmed/29980152
https://www.ncbi.nlm.nih.gov/pubmed/29980151
https://www.ncbi.nlm.nih.gov/pubmed/29980150
https://www.ncbi.nlm.nih.gov/pubmed/29980149
https://www.ncbi.nlm.nih.gov/pubmed/29980148
...

Которые, по сравнению с страницей внешнего интерфейса , находятся в том же порядке. : -)

0 голосов
/ 01 июля 2018

Контент очень динамичный, поэтому было бы лучше использовать selenium или аналогичные клиенты, но я понимаю, что это не будет практично, так как количество результатов очень велико. Итак, нам нужно проанализировать HTTP-запросы, отправленные браузером, и смоделировать их с помощью requests.

Содержимое следующей страницы загружается запросом POST в /pubmed, а данные записи являются полями ввода формы EntrezForm. Отправка формы контролируется js (запускается при нажатии кнопки «следующая страница») и выполняется методом .submit().

После некоторого осмотра я обнаружил несколько интересных полей:

  • EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_Pager.CurrPage и
    EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_Pager.cPage указывает текущую и следующую страницу.

  • EntrezSystem2.PEntrez.DbConnector.Cmd, кажется, предварительно формирует запрос к базе данных. Если мы не отправим это поле, результаты не изменятся.

  • EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_DisplayBar.PageSize и EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_DisplayBar.PrevPageSize указывает количество результатов на странице.

С этой информацией я смог получить несколько страниц с помощью приведенного ниже сценария.

import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

geturl = "https://www.ncbi.nlm.nih.gov/pubmed/?term=%222015%22%5BDate+-+Publication%5D+%3A+%223000%22%5BDate+-+Publication%5D"
posturl = "https://www.ncbi.nlm.nih.gov/pubmed/"

s = requests.session()
s.headers["User-Agent"] = "Mozilla/5.0"

soup = BeautifulSoup(s.get(geturl).text,"lxml")
inputs = {i['name']: i.get('value', '') for i in soup.select('form#EntrezForm input[name]')}

results = int(inputs['EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_ResultsController.ResultCount'])
items_per_page = 100
pages = results // items_per_page + int(bool(results % items_per_page))

inputs['EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_DisplayBar.PageSize'] = items_per_page
inputs['EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_DisplayBar.PrevPageSize'] = items_per_page
inputs['EntrezSystem2.PEntrez.DbConnector.Cmd'] = 'PageChanged'

links = []

for page in range(pages):
    inputs['EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_Pager.CurrPage'] = page + 1
    inputs['EntrezSystem2.PEntrez.PubMed.Pubmed_ResultsPanel.Pubmed_Pager.cPage'] = page

    res = s.post(posturl, inputs)
    soup = BeautifulSoup(res.text, "lxml")

    items = [i['href'] for i in soup.select("div.rslt p.title a[href]")]
    links += items

    for i in items:
        print(i)

Я запрашиваю 100 элементов на странице, потому что большие цифры, кажется, «ломают» сервер, но вы должны быть в состоянии откорректировать это число с некоторой проверкой ошибок.

Наконец, ссылки отображаются в порядке убывания (/29960282, /29960281, ...), поэтому я подумал, что мы могли бы рассчитать ссылки без предварительного выполнения любых запросов POST:

geturl = "https://www.ncbi.nlm.nih.gov/pubmed/?term=%222015%22%5BDate+-+Publication%5D+%3A+%223000%22%5BDate+-+Publication%5D"
posturl = "https://www.ncbi.nlm.nih.gov/pubmed/"

s = requests.session()
s.headers["User-Agent"] = "Mozilla/5.0"
soup = BeautifulSoup(s.get(geturl).text,"lxml")

results = int(soup.select_one('[name$=ResultCount]')['value'])
first_link = int(soup.select_one("div.rslt p.title a[href]")['href'].split('/')[-1])
last_link = first_link - results

links = [posturl + str(i) for i in range(first_link, last_link, -1)]

Но, к сожалению, результаты не точны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...