Не удалось исчерпать содержимое всех одинаковых URL-адресов, используемых в моем скрепе

Я написал скребок в python, используя библиотеку BeautifulSoup, чтобы проанализировать все имена, пересекающие разные страницы веб-сайта. Я мог бы управлять им, если бы это было не более одного URL-адреса с разной разбивкой на страницы, что означает, что некоторые URL-адреса имеют разбивку на страницы, а некоторые не так, как содержание мало.

Мой вопрос: как я могу скомпилировать их внутри функции для обработки, имеют ли они разбиение на страницы или нет?

Моя первоначальная попытка (он может анализировать содержимое только с первой страницы URL-адреса):

import requests 
from bs4 import BeautifulSoup

urls = {
    'https://www.mobilehome.net/mobile-home-park-directory/maine/all',
    'https://www.mobilehome.net/mobile-home-park-directory/rhode-island/all',
    'https://www.mobilehome.net/mobile-home-park-directory/new-hampshire/all',
    'https://www.mobilehome.net/mobile-home-park-directory/vermont/all'
}

def get_names(link):
    r = requests.get(link)
    soup = BeautifulSoup(r.text,"lxml")
    for items in soup.select("td[class='table-row-price']"):
        name = items.select_one("h2 a").text
        print(name)

if __name__ == '__main__':
    for url in urls:
        get_names(url)

Я мог бы все это сделать, если есть один URL с разбивкой на страницы, как показано ниже:

from bs4 import BeautifulSoup 
import requests

page_no = 0
page_link = "https://www.mobilehome.net/mobile-home-park-directory/new-hampshire/all/page/{}"

while True:
    page_no+=1
    res = requests.get(page_link.format(page_no))
    soup = BeautifulSoup(res.text,'lxml')
    container = soup.select("td[class='table-row-price']")
    if len(container)<=1:break 

    for content in container:
        title = content.select_one("h2 a").text
        print(title)

Но все URL-адреса не имеют разбивки на страницы. Итак, как я могу уловить все из них, есть ли какая-либо разбивка на страницы или нет?

Ответ 1

Кажется, я обнаружил очень надежное решение этой проблемы. Этот подход является итеративным. Сначала проверьте, есть ли на этой next page URL-адрес next page страницы. Если он найдет его, он будет отслеживать этот URL и повторять процесс. Однако, если какая-либо ссылка не имеет разбиения на страницы, скребок сломается и попробует другую ссылку.

Вот:

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

urls = [
        'https://www.mobilehome.net/mobile-home-park-directory/alaska/all',
        'https://www.mobilehome.net/mobile-home-park-directory/rhode-island/all',
        'https://www.mobilehome.net/mobile-home-park-directory/maine/all',
        'https://www.mobilehome.net/mobile-home-park-directory/vermont/all'
    ]

def get_names(link):
    while True:
        r = requests.get(link)
        soup = BeautifulSoup(r.text,"lxml")
        for items in soup.select("td[class='table-row-price']"):
            name = items.select_one("h2 a").text
            print(name)

        nextpage = soup.select_one(".pagination a.next_page")

        if not nextpage:break  #If no pagination url is there, it will break and try another link

        link = urljoin(link,nextpage.get("href"))

if __name__ == '__main__':
    for url in urls:
        get_names(url)

Ответ 2

Это решение пытается найти разбиение a страницы a теги. Если какая-либо разбивка на страницы найдена, все страницы очищаются, когда пользователь выполняет PageScraper над экземпляром класса PageScraper. Если нет, будет сканироваться только первый результат (одна страница):

import requests
from bs4 import BeautifulSoup as soup
import contextlib
def has_pagination(f):
  def wrapper(cls):
     if not cls._pages:
       raise ValueError('No pagination found')
     return f(cls)
  return wrapper

class PageScraper:
   def __init__(self, url:str):
     self.url = url
     self._home_page = requests.get(self.url).text
     self._pages = [i.text for i in soup(self._home_page, 'html.parser').find('div', {'class':'pagination'}).find_all('a')][:-1]
   @property
   def first_page(self):
      return [i.find('h2', {'class':'table-row-heading'}).text for i in soup(self._home_page, 'html.parser').find_all('td', {'class':'table-row-price'})]
   @has_pagination
   def __iter__(self):
     for p in self._pages:
        _link = requests.get(f'{self.url}/page/{p}').text
        yield [i.find('h2', {'class':'table-row-heading'}).text for i in soup(_link, 'html.parser').find_all('td', {'class':'table-row-price'})]
   @classmethod
   @contextlib.contextmanager
   def feed_link(cls, link):
      results = cls(link)
      try:
        yield results.first_page
        for i in results:
          yield i
      except:
         yield results.first_page

Конструктор класса найдет любую разбивку на страницы, а метод __iter__ все страницы, только если найдены ссылки на страницы. Например, https://www.mobilehome.net/mobile-home-park-directory/rhode-island/all не имеет разбивки на страницы. Таким образом:

r = PageScraper('https://www.mobilehome.net/mobile-home-park-directory/rhode-island/all')
pages = [i for i in r]

ValueError: нет разбивки на страницы

Однако содержимое первой страницы можно найти:

print(r.first_page)
['Forest Park MHP', 'Gansett Mobile Home Park', 'Meadowlark Park', 'Indian Cedar Mobile Homes Inc', 'Sherwood Valley Adult Mobile', 'Tripp Mobile Home Park', 'Ramblewood Estates', 'Countryside Trailer Park', 'Village At Wordens Pond', 'Greenwich West Inc', 'Dadson Mobile Home Estates', "Oliveira Garage", 'Tuckertown Village Clubhouse', 'Westwood Estates']

Однако для страниц с полным разбиением на страницы все результирующие страницы можно очистить:

r = PageScraper('https://www.mobilehome.net/mobile-home-park-directory/maine/all')
d = [i for i in r]

PageScraper.feed_link выполнит эту проверку автоматически и PageScraper.feed_link первую страницу со всеми последующими результатами, если будет найдена разбивка на страницы, или только первая страница, если в результате нет разбиения на страницы:

urls = {'https://www.mobilehome.net/mobile-home-park-directory/maine/all', 'https://www.mobilehome.net/mobile-home-park-directory/rhode-island/all', 'https://www.mobilehome.net/mobile-home-park-directory/vermont/all', 'https://www.mobilehome.net/mobile-home-park-directory/new-hampshire/all'}
for url in urls:
   with PageScraper.feed_link(url) as r:
      print(r)