Данные с сайта, разбор обьектов.
KOPYL DesignСегодня в чат написал парень с вопросом как ему достать такие-то элементы из сайта. И меня вперло помочь ему, заодно закрепив свои знания смачным конспектом.
Я сам относительно нередко пишу в чат с просьбой помочь мне решить какую-то проблему в программировании. Поэтому решил что теперь моя очередь помогать и предложил ему написать мне в лс. Помогу, думал я, заодно и материал закреплю.
К моменту я ответил ему ему оставалось сделать последнее – доставать заголовок из названия обьявления на OLX.UA: https://www.olx.ua/uk/obyavlenie/kurtochka-dlya-malchika-IDJgsXX.html?sd=1#ebec54ca7f;promoted .
Загвоздка заключалась в том, что название типа "Курточка для мальчика" было не единственным текстом в элементе из которого можно легко вытащить данные. Название стояло вместе с другими обьектами как цена, категория и название сайта (почему так я пока обьяснять не буду, но так делают).
Получается у нас обьект названия состоит не из "Курточка для мальчика", а из "Курточка для мальчика: 350 грн. - Одяг для хлопчиків Олександрія на Olx". А человеку нужно было вытащить только название.
Я знал лишь только как на Requests получать содержимое сайта и что его можно обрабатывать через библиотеку BeautifulSoup. Но все тонкости библиотеки я пока за 3 недели не запомнил, поэтому мне пришлось лезть в гугл и я нашел такое решение:
a_string = "ab-cd"
split_string = a_string.split("-", 1)
Split into "ab" and "cd"
substring = split_string[0] print(substring)
И адаптировал его под свою структуру код:

Берем запрос через Requests, потом говорим что soup у нас теперь будет текстовое содержимое того, что мы получили выше. Да, мы не сразу получаем содержимое страницы функцией get. Им будет page.text (у page (а технически самого запроса) есть еще штуки, которые мы можем использовать кроме самого text.
SOUP – это просто библиотека, которая позволяет нам удобно и интуитивно искать элементы в содержимомом странице более привычным языком По сути soup у нас это тот же page.text, но обработанный библиотекой (техничкески функцией) BeautifulSoap. lxml – это один из способов разбивки содержимого для лучшего доступа к нему.
for X in Y делает что мы Х скажем внизу в обьекте Y (зачастую это список) Х кол-во раз.
soup.find_all("title") создает список из всех элементов с тегом "title", которые он найдет на странице.
...
Пока рассматривал код, пытаясь обьяснять я понял что логика тут не самая лучшая, поскольку при помощи find_all("title") нахожу все теги "title" на странице, а при помоши структуры for X in Y вывожу каждый найденный элемент (х) из soup.find_all("title").
Так вот логика не самая лучшая потому что я глянул в содержимое сайта, а там только один тег "title". Получается конструкция for X in Y лишняя.
Поэтому убрал конструкцию для многочисленного перечисления обьектов:

Для наглядности верну сюда наш заголовок, с которым теперь нам предстоит работать: "Курточка для мальчика", а из "Курточка для мальчика: 350 грн. - Одяг для хлопчиків Олександрія на Olx".
Обьясню эту конструкцию: Мы берем .текст нашего тега "title" (без получения .text у нас будет просто сырой элемент из HTML – <title>Clipper Зажигалка: 250 грн. - Коллекционирование Киев на Olx</title> . А когда мы дописываем к нему .text это означает что из этого страшного обьекта нам нужен другой менее страшный обьект – текст, то есть уже "Clipper Зажигалка: 250 грн. - Коллекционирование Киев на Olx").
Теперь мы делаем следующее – разделяем название обьявления на два и вносим их в список для более удобного манипулирования при помощи soup.find("title").text.split(":",1). Второй обьект у нас это того, кторый после двоеточия в тексте – 350 грн. - Одяг для хлопчиків Олександрія на Olx. Это у нас 0. Певый обьект – тот, который до двоеточия – Курточка для мальчика. И при помощи [0] в конце мы указываем какой из этих элементов нам нужен, а какой можно выбросить. И да, в программировании отсчет начинает с 0, поэтому 0 это у нас первый обьект, а 1 – второй.
В конце выводим результат на экран при помощи print. Это у нас "Курточка для мальчика". Значит все верно.
В общем я сделал это и отправил. Но сразу понял, что эта кострукция далеко не самая надежная, поскольку если вдруг в тексте обьявления будет двоеточие, которое напишет сам человек, например "iPhone: 32 гига", то все что после двуеточия у нас выкинется. На самом деле это редкий случай, я даже не помню когда я в последний раз видел обьявление с двуеточием. То есть вместо заголовка "iPhone: 32 гига" мы получим заголовок "iPhone", что не есть хорошо.
Я опять полез в гугл, поскольку стандарнтые библиотеки и синтаксис Python я пока что знаю не очень хорошо.
Придумал запрос (а это уже половина дела сделано) – python strip after last dot. По нему вылезла первая статья с примером:
>>> s = 'POS--K 100 100 001 - 1462'
>>> s[s.rindex('-')+1:]
' 1462'
Копировать я его полностью не смог, пришлось разобраться как это работает. Благо Питон оченьт интуитивный язык программирования и я быстро все схватил и адаптировал:

Все как и в прошлом примере. Внимание стоит обратить только на все, что после soup = BeautifulSoup(page.text, "lxml":
title = soup.find("title").text
title = title[:title.rindex(":")]
print(title)
title = soup.find("title").text – вынес в переменную для удобства. И перехожу к ее переопределению. Обьясню как работает конструкция title[:title.rindex(":")]:
Мы берем текст и указываем что в ним делать в скобочках:
Разберем изнутри начиная с :
var = "wowwwd sswsjj"
print(var[var.index("s"):])
В этом случае у нас будет результат – sswsjj
А если заменить index на rindex, то вместо sswsjj у нас будет sjj. То есть index считает слева, а rindex справа (от Right index).
Вот эта конструкция с var[var...:] означает что нам нужно отобразить не порядковый номер, а набор значений до или после "s" ( var.index("s") без нашей v[v...:] конструкции выдала бы просто порядковый номер символа в нашей строчке – 7. (кажется эта конструкция являтся одним из видов comprehension (в перерводе – детальный разбор) lists). Если мы поставим : перед закрывающей квадратной скобкой в конце, то получим на выходе остаток нашей строки после первой буквы s, а если поставим в начале после открывающей [, то получим все буквы до буквы s.
Если упороться, то при помощи print(var[var.index("s"):][]) мы уже с резульататом нашей готовой конструкции можем что-то делать внутри еще одних последних скобочек. Например print(var[var.index("s"):][2]) вызовет 3-ю букву из нашей строки, print(var[var.index("s"):][1:3]) вызовет от второй по третью (включительно), а print(var[var.index("s"):][:3]) первые 3 значения. Но учтите, это уже навороты над тем, что мы сллали ранее. Пока нам это может и не надо, но как заметка тут пускай будет :)
p.s: буквально минут 10 после публикации меня в чате спросили почему я не достаю заголовок из тега h1... А элемент и правда был в h1. Я обычно перед парсингом проверяю страницу на лучшее совпадение, чтобы не делать глупую работу, но сейчас я давно не спал (сутки+ и поэтому немного завтыкал). Но в любом случае хороший опыт и неплохая статья получилась. Вот финалка:


rstrip нужен для того, чтобы убрать пробелы и переносы строк вокру нужного нам значения – названия Clipper Зажигалка, например.