Я работаю над Web Scraper, который возвращает ссылки и описания объявлений о вакансиях, если они соответствуют списку ключевых слов. У меня проблема в том, что экспортируемый csv представляет собой всего одно длинное предложение, в котором ссылка и описание идут в одну строку.
Как я могу эффективно разделить описание и ссылки на два отдельных столбца? И как я могу добавить оставшуюся часть ссылки, чтобы иметь возможность щелкнуть ссылку внутри csv? Также есть ли способ избежать дублирования записей в csv?
Вот мой код:
from selenium import webdriver
import time, re, csv
from bs4 import BeautifulSoup as BS
keywords = ["KI", "AI", "Big Data", "Data", "data", "big data",
"Analytics", "analytics", "digitalisierung", "ML",
"Machine Learning", "Daten", "Datenexperte",
"Datensicherheitsexperte", "Analytikleistungen"]
browser = webdriver.Chrome()
url = "https://ausschreibungen.usp.gv.at/at.gv.bmdw.eproc-p/public"
browser.implicitly_wait(30)
browser.get(url)
innerHTML = browser.execute_script("return
document.body.innerHTML")
soup = BS(browser.page_source, 'html.parser')
# browser.quit()
# print(soup.text)
tenders = soup.find('table', {'id': 'tenderlist'})
tbody = tenders.find('tbody')
browser.quit()
ausschreiben_found = []
for tr in tbody.find_all('tr'):
ausschreiben = tr.find_all('td')
for keyword in keywords:
for word in ausschreiben:
if keyword in str(word):
ausschreiben_found.append(word)
print(ausschreiben_found)
with open("ausschreiben.csv", 'a', encoding='utf-8') as toWrite:
fieldnames = ["Beschreibung", "Links"]
writer = csv.writer(toWrite)
writer.writerows(ausschreiben_found)
# subprocess.call('./Autopilot3.py')
print("Matched Ausschreiben have been collected.")
Я также хотел бы добавить, что вы можете получить доступ к таблице через XHR, найденный при проверке страницы.
Если ничего не добавляется, не означает ли это, что совпадений не было? На днях он вернул 2 листинга, или я ошибаюсь? Не могли бы вы подробнее рассказать о XHR?
Если вы посмотрите на Ajax-запросы, которые отправляет эта страница, то увидите, что один из них заполняет таблицу данных (.../at.gv.bmdw.eproc-p/ajax/dataTablesTenderList?...
). Он возвращает JSON. Вам может быть гораздо проще запросить это напрямую, чем идти через Selenium и пытаться анализировать HTML.
Эрик, вы правы, если ничего не было добавлено, значит ничего не найдено по этим ключевым словам. Я ожидал, так как вы опубликовали это сегодня, вы получили результаты сегодня.
Используйте параметры новой строки и разделителя класса csv.writer.
Вы можете найти примеры здесь: https://docs.python.org/3/library/csv.html#писатели-объекты
Хотя это может помочь OP, лучше добавить больше деталей, примеров и т. д. Пожалуйста, давать ответы, не требующие пояснений от спрашивающего.
писать в отдельные столбцы
reader = csv.DictReader(f) # open and write mode opened file
csvWriter = csv.writer(f)
existing_queries = set()
for row in reader:
if reader.line_num == 1:
continue
if row['link'] in existing_queries:
print("Already exists")
else:
csvWriter.writerow("description", "link") # will write
existing_queries.add("description", "link")
надеюсь, это поможет
Поскольку веб-сайт использует Ajax и библиотеку JavaScript для заполнения таблицы на странице, самый простой способ получить нужные данные — реплицировать запрос Ajax.
Данные JSON с сервера имеют следующую структуру:
{
"value": {
"draw": "-1",
"recordsTotal": 1476,
"recordsFiltered": 1476,
"data": [{
"DT_RowClass": "even",
"0": "<a href=\"/at.gv.bmdw.eproc-p/public/de_AT/tenderlist?action=view&object=41a809d9-0b61-4991-86b8-74dc07973af3-15ed14df-d91c-4905-94fd-e1d7935eaef1\">Planung Freiland/Brücke</a>",
"1": "Autobahnen- und Schnellstraßen-Finanzierungs-Aktiengesellschaft",
"2": "08.04.2019",
"3": null
}, {
"DT_RowClass": "odd",
"0": "<a href=\"/at.gv.bmdw.eproc-p/public/de_AT/tenderlist?action=view&object=86dd87bd-7426-40c5-946b-62b2af638aab-7a54478b-9e89-4d47-bdf8-dc8b867c57b8\">Lieferung von Erdgas 2020 - 2022</a>",
"1": "Republik Österreich (Bund), Bundesbeschaffung GmbH sowie alle weiteren Auftraggeber gemäß der den Ausschreibungsunterlagen beiliegenden Drittkundenliste, im Vergabeverfahren alle vertreten durch die Bundesbeschaffung GmbH",
"2": "08.04.2019",
"3": "07.05.2019"
}]
}
}
Далее используется модуль requests
для получения JSON с сервера и самый маленький анализатор HTML для извлечения текста из ссылок. Вы можете использовать BeautifulSoup для той же цели.
import requests
from html.parser import HTMLParser
class TinyTextExtractor(HTMLParser):
def parse(self, html):
self.text = ''
self.feed(html)
return self.text
def handle_data(self, data):
self.text += data
def get_ausschreibungen(start=0, length=25):
url = 'https://ausschreibungen.usp.gv.at/at.gv.bmdw.eproc-p/ajax/dataTablesTenderList'
resp = requests.get(url, {
'start': start,
'length': length
})
parser = TinyTextExtractor()
for row in resp.json()['value']['data']:
yield {
'Bezeichnung': parser.parse(row['0']),
'Organisation': row['1'],
'Veröffentlicht': row['2'],
'Frist': row['3'],
}
Применение:
for item in get_ausschreibungen(0, 3):
print(item)
Что печатает это для меня:
{'Bezeichnung': 'Planung Freiland/Brücke', 'Organisation': 'Autobahnen- und Schnellstraßen-Finanzierungs-Aktiengesellschaft', 'Veröffentlicht': '08.04.2019', 'Frist': None}
{'Bezeichnung': 'Lieferung von Erdgas 2020 - 2022', 'Organisation': 'Republik Österreich (Bund), Bundesbeschaffung GmbH sowie alle weiteren Auftraggeber gemäß der den Ausschreibungsunterlagen beiliegenden Drittkundenliste, im Vergabeverfahren alle vertreten durch die Bundesbeschaffung GmbH', 'Veröffentlicht': '08.04.2019', 'Frist': '07.05.2019'}
{'Bezeichnung': 'Umbau Bahnhof Villach ', 'Organisation': 'ÖBB-Personenverkehr AG', 'Veröffentlicht': '08.04.2019', 'Frist': None}
Я уверен, что фильтрация/превращение его в CSV больше не проблема.
Используйте инструменты разработчика браузера (F12), чтобы выяснить, какие другие параметры запроса отправляются и имеют ли они отношение к вам. Вы также можете попробовать «вписаться», используя функцию сеанса модуля requests
, реплицируя все заголовки HTTP и файлы cookie, но, учитывая, что это правительственный сайт, они, вероятно, не будут возражать, что вы их очищаете.
Большое спасибо за это! Не понимал, что могу использовать запрос Ajax для анализа данных :)
Есть ли способ разобрать ссылку href?
Как я уже сказал, вы можете использовать BeautifulSoup для разбора строки в row['0']
. Поскольку в первом столбце может быть более сложный HTML, чем просто единственное число <a>
, это в любом случае хорошая идея.
Также есть способ расширить мой «наименьший парсер HTML» для этого, если вы представляете себе вызов, вы можете попробовать сделать это (прочитайте модуль html.parser
, подсказка: вам нужно написать метод handle_starttag
). Наградой будет то, что он, вероятно, немного быстрее, чем более тяжелый BeautifulSoup, и что вы научитесь работать с парсерами, основанными на событиях. :)
когда я запускаю ваш код, он создает пустой список для ausschreiben_found (или, я должен сказать, ничего не добавляется).