Перейти к публикации
Форум ботоводов
admin

Как воровать статьи с Форклога

Рекомендованные сообщения

Сейчас будет материал, который, в общем-то, ничего не нарушает - но всё равно ближе к серой зоне. Мы будем брать новости с новостного сайта, собирать их скриптом в нужную структуру данных и публиковать у себя. Впрочем, публикация будет наверное во второй части.

Итак, в качестве жертвы возьмем Форклог- мне он нравится, я подписан на два его канала в телеграмме, думаю будет не лишним, если его новости будут дублироваться еще и тут. Удобно, знаете ли ) Ну, и кроме того, он не против - специально заскриню на всякий случай:

image.png.950353a945f9eeb1ebfa27c84e3722a6.png

Активную ссылку оставим - это вообще должно касаться любого источника данных в интернете (включая и материалы этого форума)

Итак, давайте начнем.

Первым делом думаем, как бы заполучить ссылки на последние посты - есть много вариантов, но самый простой, это проверить типичные SEO маршруты - sitemap.xml и rss.

Подставляем в URL эти волшебные слова и voila!

image.png.83f7f9702ed59c50f9081b88668e4721.png

image.png.497721688b0cdc30e31851e4c34ecc14.png

(c https://forklog.com/rss/ произошел редирект на /feed/)

Честно говоря, даже обидно, что не надо ничего толком парсить.

В sitemap.xml хранятся ссылки на вообще всё, что есть на сайте - включая посты за три года. Это тоже своего рода сокровище, конечно, и даже ультра интересная вещь для анализа, но для текущих задач такие архивы не нужны - их долго скачивать, их нужно распаковывать и т.п. Как-нибудь в другой раз. Сегодня возьмем RSS

import requests
from xml.etree import ElementTree as etree

res = requests.get("https://forklog.com/feed/")

root = etree.fromstring(res.text)
item = root.findall('channel/item')

for entry in item:   
    link = entry.findtext('link')
    title = entry.findtext('title')                     
    print(link)
    print(title)
    print('**********************')

Вот такой простой скрипт выведет все последние заголовки и ссылки на них на форклоге почти любом сайте на движке WordPress (если на другом сайте не подошло feed подставляйте rss, процентов 80, что сработает)

Результат примерно такой:

image.png.19a87dc1aa3f2f941493a98fac27c629.png

Теперь задача в том, что бы понимать, что уже своровано, а что еще нет. Первая мысль, которая может придти в голову, это куда-то записывать ссылку или title, и сравнивать с последней записью (по времени, в списке она выводится первой). 

Забудьте, если статью удалят или переименуют, всё пойдет не так. К счастью, хотя ForkLog и прячет ссылки за ЧПУ, все равно можно узнать ID каждой новости - в том же rss есть т.н. пермалинки - постоянные адреса страниц, которые ни от чего не зависят, вот такие:

<guid isPermaLink="false">https://forklog.com/?p=61294</guid>

Вот их и заюзаем.

Скрипт будет выглядеть так - получить список новостей, выбрать все, чей ID выше последнего запомненного, запомнить последний. Вот так:

import os
import re
import requests
from xml.etree import ElementTree as etree

# Сюда будем писать последнее полученное ID 
last_id_file = "./forklog_last_id"
last_id = 0

# Если файла не существует, то создадим и запишем туда 0
if not os.path.exists(last_id_file):
    with open(last_id_file, "w") as out:
        out.write(str(last_id))
else:
    with open(last_id_file, 'r+') as inp:
        last_id = int(inp.read())

# Получить содержимое feed-ленты
res = requests.get("https://forklog.com/feed/", verify=False)

# Разобрать полученный xml на элементы
root = etree.fromstring(res.text)
item = root.findall('channel/item')

new_last_id = last_id
for pos, entry in enumerate(item):
    try:
        # Вычленить ID новости
        permalink = entry.findtext('guid')
        post_id = int(re.sub("\D", "", permalink))
        
        link = entry.findtext('link')
        title = entry.findtext('title')

        # Текущая новость уже была, нет смысла смотреть дальше
        if post_id <= last_id:
            print("Наткнулся на старую новость, выход\n", title, link )
            break
        if pos == 0:
            # т.к. мы уйдем ниже по списку новостей, запомним самую последнюю
            new_last_id = post_id
        print(link)
        print(post_id, permalink)   
        print(title)
        print('**********************')
        
    except Exception as e:
        print('Что то пошло не так', e)

# Обновим записанное в файле значение, что бы потом не трогать старые новости
if new_last_id != last_id:
     with open(last_id_file, "w") as out:
        out.write(str(new_last_id))

Всё, получили данные, теперь можно их куда-нибудь вставлять. А скрипт можно запускать по планировщику, каждые 10 минут, например.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Итак, теперь мы можем получать ссылки на самые последние статьи. Но нам-то нужен полный текст новости со всем содержимым - его не будет в RSS. Давайте парсить страницы. Логика усложнится - теперь мы для каждой новой новости (пфф) будем получать текст её страницы, и разбирать её на составляющие. Можно делать это регулярными выражениями или вообще голой логикой в стиле программирования на C, но мы пойдем ленивым путем и поставим модуль beautifulsoup4

pip install beautifulsoup4

И давайте испытывать её в деле.

Откроем исходный код страницы любой новости с сайта и изучим. Найдем интересующие места:

image.thumb.png.c27d343595ea4751ecc344222dac5f01.png

Нас интересует всё, что есть в тегах <p> в теге <section id="article_content">. Давайте просто это заберем

import os
import re
import time
from bs4 import BeautifulSoup
import requests
from xml.etree import ElementTree as etree

# Сюда будем писать последнее полученное ID 
last_id_file = "./forklog_last_id"
last_id = 0

# Если файла не существует, то создадим и запишем туда 0
if not os.path.exists(last_id_file):
    with open(last_id_file, "w") as out:
        out.write(str(last_id))
else:
    with open(last_id_file, 'r+') as inp:
        last_id = int(inp.read())

# Получить содержимое feed-ленты
res = requests.get("https://forklog.com/feed/", verify=False)

# Разобрать полученный xml на элементы
root = etree.fromstring(res.text)
item = root.findall('channel/item')

new_last_id = last_id
for pos, entry in enumerate(item):
    try:
        # Вычленить ID новости
        permalink = entry.findtext('guid')
        post_id = int(re.sub("\D", "", permalink))
        
        link = entry.findtext('link')
        title = entry.findtext('title')


        

        # Текущая новость уже была, нет смысла смотреть дальше
        if post_id <= last_id:
            print("Наткнулся на старую новость, выход\n", title, link )
            break
        if pos == 0:
            # т.к. мы уйдем ниже по списку новостей, запомним самую последнюю
            new_last_id = post_id

        news_res = requests.get(link, verify=False)
        soup = BeautifulSoup(news_res.text, 'html.parser')
        
        article_text = ''
        article = soup.find("section", {"id":"article_content"}).findAll('p')
        
        for element in article:
            article_text += '\n' + ''.join(element.findAll(text = True))
        
        print(link)
        print(post_id, permalink)   
        print(title)
        
        print('-'*80)
        print(article_text)
        print('-'*80)

        print('**********************')
        # Что бы не мешать частыми запросами и что бы не забанили
        time.sleep(2)
    except Exception as e:
        print('Что то пошло не так', e)

# Обновим записанное в файле значение, что бы потом не трогать старые новости
if new_last_id != last_id:
     with open(last_id_file, "w") as out:
        out.write(str(new_last_id))

Готово, теперь бот может забирать тексты статей. Изображения будут тянуться напрямую с сайта Форклога. Конечно, это не очень хорошо, и надо бы их перекачивать себе, но пусть пока что будет так :)

Результат выглядит вот так (статья сменилась, пока тестировал):

image.png.904158d3d1cd79d748868da3253d9bf3.png

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ну, а теперь последний шаг - давайте постить всё найденное на форум (IPB в данном случае). В админке я создал API ключ для своего скрипта (но вам не покажу), и познал сокрушительный удар - оказывается, Invision прячется за CloudFront, а те режут Post запросы, поля которых содержат больше 2кб.... Ну что ж делать, не выбрасывать же скрипт.. Теперь будут вороваться те новости, в которых меньше 2000 байт :)

import os
import re
import time
from bs4 import BeautifulSoup
import requests
from xml.etree import ElementTree as etree

forum_id = 13
api_key = '....'
author_id=3
max_text_size=2000


# Сюда будем писать последнее полученное ID 
last_id_file = "./forklog_last_id"
last_id = 0

# Если файла не существует, то создадим и запишем туда 0
if not os.path.exists(last_id_file):
    with open(last_id_file, "w") as out:
        out.write(str(last_id))
else:
    with open(last_id_file, 'r+') as inp:
        last_id = int(inp.read())

# Получить содержимое feed-ленты
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}

res = requests.get("https://forklog.com/feed/", headers=headers, verify=False)

# Разобрать полученный xml на элементы
root = etree.fromstring(res.text)
item = root.findall('channel/item')



new_last_id = last_id
for pos, entry in enumerate(item):
    try:
        # Вычленить ID новости
        permalink = entry.findtext('guid')
        post_id = int(re.sub("\D", "", permalink))
        
        link = entry.findtext('link')
        title = entry.findtext('title')


        

        # Текущая новость уже была, нет смысла смотреть дальше
        if post_id <= last_id:
            print("Наткнулся на старую новость, выход\n", title, link )
            break
        if pos == 0:
            # т.к. мы уйдем ниже по списку новостей, запомним самую последнюю
            new_last_id = post_id

        news_res = requests.get(link, verify=False)
        soup = BeautifulSoup(news_res.text, 'html.parser')
        
        article_text = ''
        article = soup.find("section", {"id":"article_content"}).findAll('p')
        
        for element in article:
            #article_text += '\n' + ''.join(element.findAll(text = True))
            article_text += str(element)
        
        print(link)
        print(post_id, permalink)   
        print(title)
        
        print('-'*80)
        print(len(article_text), article_text)
        print('-'*80)
        if article_text:
            article_text += '<p><a href="'+link+'" target="_blank">Ссылка на оригинал статьи</a></p>'
            
        if len(article_text) < max_text_size:
            
            
            res = requests.post(
                'https://forum.bablofil.ru/api/forums/topics?key=' + api_key,
                params={
                    'forum':forum_id,
                    'author':author_id,
                    'title':title,
                    'post':article_text
                },
                headers=headers,
                verify=False
            )
            print(res.text)
            
        print('**********************')
        # Что бы не мешать частыми запросами и что бы не забанили
        time.sleep(13)
    except Exception as e:
        print('Что то пошло не так', e)

# Обновим записанное в файле значение, что бы потом не трогать старые новости
if new_last_id != last_id:
     with open(last_id_file, "w") as out:
        out.write(str(new_last_id))

А результат вы можете видеть в разделе новостей на этом форуме. 

За сим всё, оставил скрипт работать на сервере. Надеюсь, будет кому-то полезным

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Нет ни одного ресурса где так подробно по кусочкам разбирают это дело , главное понятно даже новичку в этом деле , доходчиво все ) Посмотрел пару курсов по змейке , именно то что я хотел , просмотрел много уроков на ютубе но там те кто объясняют делают это лениво и с таким голосом что сразу хочется спать ... Респект автору ! спасибо за труд ! ждем продолжения 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Этот способ дает ошибки на других сайтах - особенно повидемому из-за ошибки в символах неразрешенных в XLM.

Как прочитать новости например отсюда: https://www.six-group.com/exchanges/news/overview_de.html
Еще как прочитать новости с помощью запроса

conn = http.client.HTTPConnection(…..)
conn.request(http_method,......, headers)
response = conn.getresponse().read()

raw строку получаю, а как расшифровать не могу - ошибка вылетает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
27.11.2018 в 13:48, Nikolaev Nikolay сказал:

Этот способ дает ошибки на других сайтах - особенно повидемому из-за ошибки в символах неразрешенных в XLM.

Как прочитать новости например отсюда: https://www.six-group.com/exchanges/news/overview_de.html
Еще как прочитать новости с помощью запроса

conn = http.client.HTTPConnection(…..)
conn.request(http_method,......, headers)
response = conn.getresponse().read()

raw строку получаю, а как расшифровать не могу - ошибка вылетает.

Этот сайт не на движке WordPress, а Adobe Experience Manager, это относительно редкий движок, для них нужно отдельно скрипт писать, сначала распарсить на ссылки меню с этой страницы, потом перейти по каждой ссылке и парсить ссылки на тех страницах, пока не встретятся страницы с контентом. В общем, это разовая большая задача.

Насчет второго вопроса - 

res_text = response.decode('utf-8')
print(res_text)

Ну а ошибки можно перехватывать через try/except, игнорировать или обрабатывать, смотря что за ошибки

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Гость
Ответить в тему...

×   Вставлено в виде отформатированного текста.   Вставить в виде обычного текста

  Разрешено не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.


×
×
  • Создать...