Извлечение ссылок

Извлечение всех ссылок из веб-страницы HTML

reqwest-badge select-badge cat-net-badge

Используется reqwest::get для выполнения HTTP GET-запроса и затем используется метод Document::from_read для разбора ответа как HTML документа. find с условием, что 'Name должен быть "a"' извлекает все ссылки. Вызов filter_map на результате Selection извлекает все URL адреса из ссылок с атрибутом "href", атрибуты можно получить с помощью метода attr (атрибут).

#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate select;

use select::document::Document;
use select::predicate::Name;

error_chain! {
   foreign_links {
       ReqError(reqwest::Error);
       IoError(std::io::Error);
   }
}

fn main() -> Result<()> {
    let res = reqwest::get("https://www.rust-lang.org/en-US/")?;

    Document::from_read(res)?
        .find(Name("a"))
        .filter_map(|n| n.attr("href"))
        .for_each(|x| println!("{}", x));

    Ok(())
}

Проверка веб-страницы на наличие неработающих ссылок

reqwest-badge select-badge url-badge cat-net-badge

В примере вызывается функция get_base_url чтобы получить базовый URL адрес. Если в документе найден нужный тэг, можно затем получить атрибут href через attr из базового тэга. Position::BeforePath для изначального URL адреса работает как адрес по умолчанию.

Затем производится итерация по ссылкам в документе и разбор с помощью url::ParseOptions и Url::parse). Далее выполняются запросы по полученным ссылкам с помощью функций из крейта reqwest и у соответствующих ответов проверяются его StatusCode.

#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate select;
extern crate url;

use std::collections::HashSet;

use url::{Url, Position};
use reqwest::StatusCode;
use select::document::Document;
use select::predicate::Name;

error_chain! {
  foreign_links {
      ReqError(reqwest::Error);
      IoError(std::io::Error);
      UrlParseError(url::ParseError);
  }
}

fn get_base_url(url: &Url, doc: &Document) -> Result<Url> {
    let base_tag_href = doc.find(Name("base")).filter_map(|n| n.attr("href")).nth(0);

    let base_url = base_tag_href.map_or_else(
        || Url::parse(&url[..Position::BeforePath]),
        Url::parse,
    )?;

    Ok(base_url)
}

fn check_link(url: &Url) -> Result<bool> {
    let res = reqwest::get(url.as_ref())?;

    Ok(res.status() != StatusCode::NOT_FOUND)
}

fn main() -> Result<()> {
    let url = Url::parse("https://www.rust-lang.org/en-US/")?;

    let res = reqwest::get(url.as_ref())?;
    let document = Document::from_read(res)?;

    let base_url = get_base_url(&url, &document)?;

    let base_parser = Url::options().base_url(Some(&base_url));

    let links: HashSet<Url> = document
        .find(Name("a"))
        .filter_map(|n| n.attr("href"))
        .filter_map(|link| base_parser.parse(link).ok())
        .collect();

    links
        .iter()
        .filter(|link| check_link(link).ok() == Some(false))
        .for_each(|x| println!("{} is broken.", x));

    Ok(())
}

Извлечение всех уникальных ссылок из разметки MediaWiki

reqwest-badge regex-badge cat-net-badge

Данный пример вытягивает исходный код MediaWiki-страницы используя reqwest::get и затем ищет все внутренние и внешние ссылки с помощью регулярных выражений и метода Regex::captures_iter. Используется Cow, чтобы избежать излишних выделений объектов String на куче.

Синтаксис ссылок MediaWiki описан здесь.

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate lazy_static;
extern crate reqwest;
extern crate regex;

use std::io::Read;
use std::collections::HashSet;
use std::borrow::Cow;
use regex::Regex;

error_chain! {
    foreign_links {
        Io(std::io::Error);
        Reqwest(reqwest::Error);
        Regex(regex::Error);
    }
}

fn extract_links(content: &str) -> Result<HashSet<Cow<str>>> {
    lazy_static! {
        static ref WIKI_REGEX: Regex =
            Regex::new(r"(?x)
                \[\[(?P<internal>[^\[\]|]*)[^\[\]]*\]\]    # internal links
                |
                (url=|URL\||\[)(?P<external>http.*?)[ \|}] # external links
            ").unwrap();
    }

    let links: HashSet<_> = WIKI_REGEX
        .captures_iter(content)
        .map(|c| match (c.name("internal"), c.name("external")) {
            (Some(val), None) => Cow::from(val.as_str().to_lowercase()),
            (None, Some(val)) => Cow::from(val.as_str()),
            _ => unreachable!(),
        })
        .collect();

    Ok(links)
}

fn main() -> Result<()> {
    let mut content = String::new();
    reqwest::get(
        "https://en.wikipedia.org/w/index.php?title=Rust_(programming_language)&action=raw",
    )?
        .read_to_string(&mut content)?;

    println!("{:#?}", extract_links(&content)?);

    Ok(())
}