Не удалось исчерпать содержимое всех одинаковых 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)