Автор оригинала: Alexandre Robin.
Почему я сделал это?
Моя подруга пишет бумагу о восприятии французской музыки хип-хоп через время. Для этого она хотела бы помириться в текстовые шахты из Lemonde.fr, французская основная газета.
Проблема : Было больше, чем 7000 статей Говоря о хип-хоп музыке с 80-х годов.
Давайте код!
Для этой программы я буду использовать:
- Скрипт местных нодей
- фшс: Чтобы написать файл json
- Запрос : сделать сообщение и получить Запросы
- Cheerio : загрузить HTML и иметь возможность взаимодействовать с ним
//To install cheerio :npm i --save cheerio
Убедитесь, что у вас все это в вашем пакете. Джусон, и все должно быть хорошо:)
Что я хотел? В конце концов, я хотел, чтобы файл Excel организован, как это:
Поэтому я должен был использовать JSON Structured, как это. Я покажу вам в конце этой статьи, как конвертировать JSON в Excel.
[
{
date:,
title:,
description:,
text:,
url:,
},
]Первый шаг: получите URL все статьи
Первый шаг был довольно легким. Благодаря расширенной функции поиска, мне просто пришлось получить ссылку URL страницы результата и сообщите моим коде, как:
- Ищите количество результатов
- Рассчитайте количество страниц, зная, что на странице 30 статей
- Получите название, описание, дату и URL-адрес 30 статей для каждой страницы
Вот код для этого:
const fs = require("fs");
const request = require("request");
const cheerio = require("cheerio");
const jsonTab = []; // We create our table
function writeFile() {
// Will write the json file
fs.writeFile("output.json", JSON.stringify(jsonTab, null, 4), (err) => {
console.log("File successfully written!");
});
}
// The URL of the advanced search feature with our keywords
const url = 'http://www.lemonde.fr/recherche/?keywords="Rap+"+"hip-hop"+"hip%20hop"+"rappeur"+"rappeurs"+"raps"+"rappers"&page_num=1&operator=or&exclude_keywords=&qt=recherche_texte_title&author=&period=custom_date&start_day=01&start_month=01&start_year=1970&end_day=20&end_month=09&end_year=2017&sort=asc';
/* The first request call, our goal here is to get the number of results and then
to calculate the number of pages */
request(url, (error, response, html) => {
const $ = cheerio.load(html);
// All the variables we will use later
let number;
let description;
let date;
let title;
let link;
if (!error) {
$(".bg_gris_clair").filter(() => {
/* We want to select all the HTML
elements with the class ".bg_gris_clair" (and we already know there is
only one) */
const data = $(this);
const str = data.children("strong").text().trim();
number = parseInt(str.substring(0, str.indexOf("e")).replace(/\s/g, ""), 10);
});
}
let count = 1;
for (let i = 1; i <= number / 10; i++) {
const urlPerPage = 'http://www.lemonde.fr/recherche/?keywords="Rap+"+"hip-hop"+"hip%20hop"+"rappeur"+"rappeurs"+"raps"+"rappers"&page_num=' + i + "&operator=or&exclude_keywords=&qt=recherche_texte_title&author=&period=custom_date&start_day=01&start_month=01&start_year=1970&end_day=20&end_month=09&end_year=2017&sort=asc";
request(urlPerPage, (err, response2, html2) => {
if (!err) {
const $ = cheerio.load(html2);
$(".grid_11.omega.resultat").filter(() => {
const json = {
date: "",
title: "",
description: "",
url: ""
};
const data = $(this);
title = data.children("h3").children("a").text().trim();
link = "http://lemonde.fr" + data.children("h3").children("a").attr("href").trim();
description = data.children("p").text().trim();
const dateStr = data.children("span").text();
date = dateStr.replace(/.+?(?=\d)/, "");
json.title = title;
json.url = link;
json.description = description;
json.date = date;
jsonTab.push(json);
});
} else if (err) {
console.log(err);
}
count += 1;
// Write the file once we iterated through all the pages.
if (count === parseInt(number / 10, 10)) {
writeFile();
}
});
}
});Однажды я сделал это, у меня был файл JSON с более чем 7000 записей. Для каждого из них у меня было:
- Свидание
- Заголовок
- Описание
- URL-адрес
Мне просто не хватало контента …
” Хорошо, мне просто нужно использовать тот же код и запустить его для 7000 URL-адресов, которые я должен получить контент! »
Я принимал активно участвовать в коде на один год сейчас … И одно из первых, что я узнал, был: Ничто никогда не просто в коде Отказ Всегда. Но для каждой проблемы вы Будет ли Борьба с, есть вопрос, который вы можете Google ;-).
Я обнаружил, что огромная часть изделий не была доступна без премиум-аккаунта. Поэтому я должен был быть связан с возможностью просмотра контента и соскребать его.
К счастью, нам удалось получить премиум-аккаунт. Мне просто пришлось найти способ сказать свой код, как:
- Аутентифицироваться на Lemonde.fr.
- Оставаться на связи во время соскабливания
Шаг вторая: как аутентифицировать на веб-сайте
Для этого мне нужно было понять, как работает сайт, когда я нажимаю на «Войти». ” Хорошая новость: у нас есть инструменты разработчика.
Мне просто нужно было узнать, как сайт отправляет пароль и имя пользователя на сервер и воспроизвести шаблон.
Вот страница аутентификации LEMONDE.FR (как это французская платформа, я перевел несколько слов, чтобы помочь вам понять):
Теперь, что происходит, когда мы пытаемся войти?
Ты это видел? Я нажал на «Логин» и Lemonde.fr Отправляет почтовый запрос с простой формой, содержащей пять битов информации:
- Соединение [Mail]
- Соединение [пароль]
- Соединение [acte_connected]: 1 for true, 0 для false (hint: вы хотите, чтобы он был правдой)
- Соединение [Сохранить] Требуется здесь
- Соединение [токен] – это сложная часть
Мы уже знаем четыре бита информации из пяти. Мы просто должны найти, откуда приходит «токен».
К счастью, Lemonde.fr приятно нам ☺️:
Токен подключения автоматически генерируется в скрытом входе при запуске страницы в первый раз. Вы просто должны это знать и получить его, прежде чем пытаться войти в систему.
Ну, мы теперь готовы перейти к шагу 3!
Шаг три: должен ловить их все!
Вот полный код для аутентификации, извлечения и хранения файлов cookie и, наконец, собирать все статьи.
const fs = require("fs");
const request = require("request");
const cheerio = require("cheerio");
// Prepare all the variables needed later
let count = 0;
let timeout = 0;
const id = "myusername";
const mdp = "mypassword";
let obj;
// The URLs we will scrape from
const connexionUrl = "https://secure.lemonde.fr/sfuser/connexion";
// Will write an "output.json" file
function writeFile() {
fs.writeFile("output.json", JSON.stringify(obj, null, 4), (err) => {
console.log(
"File successfully written! - Check your project directory for the output.json file"
);
});
}
// creating a clean jar to store the cookies
const j = request.jar();
// First Get Request Call
request(
{
url: connexionUrl,
jar: j
},
(err, httpResponse, html) => {
const $ = cheerio.load(html);
// We use Cheerio to load the HTML and be able to find the connection__token
const token = $("#connection__token")[0].attribs.value; // here is the connection__token
// Construction of the form required in the POST request to login
const form = {
"connection[mail]": id,
"connection[password]": mdp,
"connection[stay_connected]": 1,
"connection[save]": "",
"connection[_token]": token
};
// POST REQUEST to Log IN. Same url with "request headers" and the complete form.
request.post(
{
url: connexionUrl,
jar: j,
headers: {
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4",
"Cache-Control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded",
Origin: "http://secure.lemonde.fr/",
Host: "secure.lemonde.fr",
"Upgrade-Insecure-Requests": 1,
"User-Agents":
"Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0",
Connection: "keep-alive",
Pragma: "no-cache",
Referer: "https://secure.lemonde.fr/sfuser/connexion"
},
form: form
},
(error, response, body) => {
// WE ARE CONNECTED :D
/* Second GET request call : this time, we use the response of the POST
request to request the right URL */
request(
{
url: response.headers.location,
jar: j
},
(err, httpResponse, html2) => {
const json = fs.readFileSync("./firstStep.json"); // Load the JSON created in step one
obj = JSON.parse(json); // We create our JSON in a usable javascript object
// forEach loop to iterate through all the object and request each link
obj.forEach((e) => {
let articleUrl = e.url;
/* We use a setTimeout to be sure that all the requests are performed
one by one and not all at the same time */
setTimeout(() => {
request(
{
url: articleUrl,
jar: j
},
(error1, httpResponse, html3) => {
if (!error1) {
const $ = cheerio.load(html3); // load the HTML of the article page
$(".contenu_article.js_article_body").filter(() => {
const data = $(this);
// get the content, remove all the new lines (better for Excel)
let text = data
.text()
.trim()
.replace(/\n/g, "\t");
e.text = text; // push the content in the table
});
$(".txt3.description-article").filter(() => {
const data = $(this);
const description = data
.text()
.trim()
.replace(/\n/g, "\t");
e.description = description;
});
}
}
);
count += 1;
// Write a new JSON file once we get the content of all the articles
if (count === obj.length) {
writeFile();
}
}, timeout);
timeout += 50; // increase the timeout length each time
});
}
);
}
);
}
);
Теперь у меня есть файл JSON со всеми статьями и их содержанием. Последний шаг – преобразовать его в реальную таблицу Excel.
Бонус шаг четвертый: От .json до .csv
Вот простой код для преобразования файла «yource.json» на «yource.csv» (вы можете поблагодарить моего друга @jvdsande. ):
const fs = require('fs');
let jsonstring = fs.readFileSync('output.json') // load the output.json file
let json = JSON.parse(jsonstring)
function JSONtoCSV(JSON) {
let CSV = ''
Object.keys(JSON[0]).forEach((key) => {
CSV += key + '§'
})
CSV += '\r\n'
JSON.forEach((obj) => {
Object.keys(obj).forEach((key) => {
CSV += obj[key] + '§'
})
CSV += '\r\n'
})
return CSV
}
fs.writeFileSync('output.csv', JSONtoCSV(json))И это так. Я могу импортировать свой файл «Output.csv» в Excel, и у меня есть то, что я хотел: 7000+ строк заполнены статьями из Lemonde.fr
Вы хотите узнать самую лучшую часть? Я уверен, что эта логика легко многоразовая для всех газетных сайтов в мире!
Если вы хотите создать базу данных или Scrape веб-сайт, не стесняйтесь обращаться ко мне через Twitter или LinkedIn, я был бы рад помочь вам.
Ой! И я работаю над боковым проектом, чтобы повторно повторно повторно использовать все, что я узнал здесь с LinkedIn, чтобы улучшить скорость поиска для рекрутеров:)
Спасибо за чтение, это моя первая история на среднем, и я был бы взволнован, чтобы узнать ваше мнение об этом!
Оригинал: “https://www.freecodecamp.org/news/how-i-scraped-7000-articles-from-a-newspaper-website-using-node-1309133a5070/”