Как создать цикл разбивки на страницы, который очищает определенное количество страниц (страницы меняются изо дня в день)

#python-3.x #web-scraping #beautifulsoup #pagination #python-requests-html

#python-3.x #очистка веб-страниц #beautifulsoup #разбивка на страницы #python-запросы-html

Вопрос:

Краткие сведения

Я работаю над своим проектом колледжа по управлению цепочками поставок и хочу анализировать ежедневные публикации на веб-сайте, чтобы анализировать и документировать спрос отрасли на услуги / продукты. Конкретная страница, которая меняется каждый день и с разным количеством контейнеров и страниц :

https://buyandsell.gc.ca/procurement-data/search/site?f[0]=sm_facet_procurement_data:data_data_tender_noticeamp;f[1]=dds_facet_date_published:dds_facet_date_published_today

Bacground

Код генерирует csv-файл (не обращайте внимания на заголовки) путем очистки HTML-тегов и документирования точек данных. Пытался использовать цикл ‘for’, но код по-прежнему сканирует только первую страницу.

Уровень знаний Python: новичок, изучите «трудный путь» через YouTube и поиск в Google. Нашел пример, который сработал для моего уровня понимания, но возникли проблемы с объединением различных решений людей.

Код на данный момент

импортируйте bs4 из urllib.request импортируйте urlopen как uReq из bs4 импортируйте BeautifulSoup как soup

проблема начинается здесь

 for page in range (1,3):my_url = 'https://buyandsell.gc.ca/procurement-data/search/site?f[0]=sm_facet_procurement_data:data_data_tender_noticeamp;f[1]=dds_facet_date_published:dds_facet_date_published_today'

uClient = uReq(my_url)
page_html = uClient.read()
uClient.close()
page_soup = soup(page_html, "html.parser")
containers = page_soup.findAll("div",{"class":"rc"})
  

в этой части не выполняется запись в дополнение к существующим позициям

 filename = "BuyandSell.csv"
f = open(filename, "w")
headers = "Title, Publication Date, Closing Date, GSIN, Notice Type, Procurement Entityn"
f.write(headers)

  
 for container in containers:
    Title = container.h2.text

    publication_container = container.findAll("dd",{"class":"data publication-date"})
    Publication_date = publication_container[0].text

    closing_container = container.findAll("dd",{"class":"data date-closing"})
    Closing_date = closing_container[0].text

    gsin_container = container.findAll("li",{"class":"first"})
    Gsin = gsin_container[0].text

    notice_container = container.findAll("dd",{"class":"data php"})
    Notice_type = notice_container[0].text

    entity_container = container.findAll("dd",{"class":"data procurement-entity"})
    Entity = entity_container[0].text

    print("Title: "   Title)
    print("Publication_date: "   Publication_date)
    print("Closing_date: "   Closing_date)
    print("Gsin: "   Gsin)
    print("Notice: "   Notice_type)
    print("Entity: "   Entity)

    f.write(Title   ","  Publication_date   ","  Closing_date   ","  Gsin   ","  Notice_type   ","  Entity  "n")

f.close()

  

Пожалуйста, дайте мне знать, если вы хотели бы видеть дальше. Rest определяет контейнеры данных, которые можно найти в HTML-коде и распечатать в csv.Любая помощь / совет будут высоко оценены. Спасибо!

Фактические результаты :

Код генерирует CSV-файл только для первой страницы.

Код не записывается поверх того, что уже было отсканировано (изо дня в день), по крайней мере

Ожидаемые результаты :

Код сканирует следующие страницы и распознает, когда нет страниц для просмотра.

Файл CSV будет генерировать 10 строк csv на страницу. (И какое бы количество ни было на последней странице, поскольку число не всегда равно 10).

Код будет написан поверх того, что уже было очищено (для более продвинутой аналитики с использованием инструментов Excel с историческими данными)

Комментарии:

1. первое, что я замечаю, это f = open(filename, "w") . Когда вы используете mode w , он перезаписывается. Поскольку вы хотите добавить к существующим данным, вы хотели бы изменить это на a . Во-вторых, вам нужно выполнить итерацию по страницам, а затем записать их. На данный момент не похоже, что вы переходите с одной страницы html на следующую. Хотелось бы, чтобы для этого был API, но, похоже, это будет грубая сила. Я поработаю над этим, чтобы помочь вам

2. Спасибо, chitown88! Хотите, чтобы я опубликовал оставшуюся часть кода? (спецификация контейнеров, которые нужно найти и записать в csv)

3. о да. это было бы полезно

4. Сообщение обновлено. Смотрите выше. Спасибо!

5. Я пошел дальше и сделал это без этого, так что вы можете вернуться, если хотите, и изменить мой

Ответ №1:

Некоторые могут сказать, что использование pandas является излишеством, но лично мне удобно им пользоваться, и мне просто нравится использовать его для создания таблиц и записи в файл.

также, вероятно, существует более надежный способ перехода от страницы к странице, но я просто хотел донести это до вас, и вы сможете с этим работать.

На данный момент я просто жестко прописываю значение следующей страницы (и я просто произвольно выбрал максимум 20 страниц), поэтому он начинается со страницы 1, а затем проходит через 20 страниц (или останавливается, как только доходит до недопустимой страницы).

 import pandas as pd
from bs4 import BeautifulSoup
import requests
import os

filename = "BuyandSell.csv"

# Initialize an empty 'results' dataframe
results = pd.DataFrame()

# Iterarte through the pages
for page in range(0,20):
    url = 'https://buyandsell.gc.ca/procurement-data/search/site?page='   str(page)   'amp;f[0]=sm_facet_procurement_data:data_data_tender_noticeamp;f[1]=dds_facet_date_published:dds_facet_date_published_today'

    page_html = requests.get(url).text
    page_soup = BeautifulSoup(page_html, "html.parser")
    containers = page_soup.findAll("div",{"class":"rc"})

    # Get data from each container
    if containers != []:
        for each in containers:
            title = each.find('h2').text.strip()
            publication_date = each.find('dd', {'class':'data publication-date'}).text.strip()
            closing_date = each.find('dd', {'class':'data date-closing'}).text.strip()
            gsin = each.find('dd', {'class':'data gsin'}).text.strip()
            notice_type = each.find('dd', {'class':'data php'}).text.strip()
            procurement_entity = each.find('dd', {'data procurement-entity'}).text.strip()

            # Create 1 row dataframe
            temp_df = pd.DataFrame([[title, publication_date, closing_date, gsin, notice_type, procurement_entity]], columns = ['Title', 'Publication Date', 'Closing Date', 'GSIN', 'Notice Type', 'Procurement Entity'])

            # Append that row to a 'results' dataframe
            results = results.append(temp_df).reset_index(drop=True)
        print ('Aquired page '   str(page 1))

    else:
        print ('No more pages')
        break


# If already have a file saved
if os.path.isfile(filename):

    # Read in previously saved file
    df = pd.read_csv(filename)

    # Append the newest results
    df = df.append(results).reset_index()

    # Drop and duplicates (incase the newest results aren't really new)
    df = df.drop_duplicates()

    # Save the previous file, with appended results
    df.to_csv(filename, index=False)

else:

    # If a previous file not already saved, save a new one
    df = results.copy()
    df.to_csv(filename, index=False)
  

Комментарии:

1. Chitown88, большое вам спасибо. Частично имеет смысл, поскольку я смотрю на это. Я постараюсь поработать с этим и вернусь, если у меня возникнут дополнительные вопросы. Действительно ценю вашу помощь! Спасибо!

2. прохладный. обязательно примите ответ, если он удовлетворил ваши потребности

3. Как мне это сделать? 🙂

4. треугольниками вверх и вниз (с большим 0) по моему решению. Должна быть галочка или что-то подобное, что позволяет вам принять ответ

5. Привет @chitown88 — Код работает идеально, спасибо за вашу помощь. Один дополнительный вопрос — Как бы мне добавить функцию, которая позволяет мне использовать учетные данные для автоматического входа на веб-сайты, требующие учетных данных? Должен ли я опубликовать это под новым вопросом? Пожалуйста, дайте мне знать.