【问题标题】:Elixir script using Floki and HttPotion fails to parse url使用 Floki 和 HttPotion 的 Elixir 脚本无法解析 url
【发布时间】:2015-11-17 02:58:40
【问题描述】:

我正在尝试使用FlokiHttPotion 为维基百科的文章文本编写脚本。我的失败代码如下所示:

defmodule Scraper do

  def start do
    base = "https://en.wikipedia.org"
    response = HTTPotion.get base <> "/wiki/Main_Page"
    html = response.body
    main_bg = Floki.find(html, ".MainPageBG")
    main_bg
      |> Floki.find("table tr li a")
      |> Floki.attribute("href")
      |> Enum.map(fn(addr) -> HTTPotion.get(base <> addr) end)
  end
end

我引用了 Floki 自述文件中的一些内容:

html
|> Floki.find(".pages a")
|> Floki.attribute("href")
|> Enum.map(fn(url) -> HTTPoison.get!(url) end)

当我将结果通过管道传输到 Floki.attribute("href") 时,我会得到一个很好的 url 路径名称列表,例如:

["/wiki/Japanese_aircraft_carrier_Hiry%C5%ABwow",
 "/wiki/Boys_Don%27t_Cry_(film)wow", "/wiki/Elias_Abraham_Rosenbergwow",
 "/wiki/Japanese_aircraft_carrier_Hiry%C5%ABwow",
 "/wiki/Boys_Don%27t_Cry_(film)wow", "/wiki/Elias_Abraham_Rosenbergwow",
 "/wiki/Wikipedia:Today%27s_featured_article/November_2015wow",
 "https://lists.wikimedia.org/mailman/listinfo/daily-article-lwow",
 "/wiki/Wikipedia:Featured_articleswow", "/wiki/Schloss_Krobnitzwow",
 "/wiki/Prussiawow", "/wiki/Albrecht_von_Roonwow", "/wiki/Harry_Winerwow",
 "/wiki/Rob_Thomas_(writer)wow", "/wiki/Of_Vice_and_Menwow",
 "/wiki/Veronica_Marswow", "/wiki/Meithalunwow", "/wiki/Palestinian_peoplewow",
 "/wiki/Marj_Sanurwow", "/wiki/Soma_Norodomwow",...]

但是,当|&gt; Enum.map(fn(addr) -&gt; HTTPotion.get(base &lt;&gt; addr) end) 行运行时,我收到此错误:

** (HTTPotion.HTTPError) {:url_parsing_failed, {:error, :invalid_uri}}
    (httpotion) lib/httpotion.ex:209: HTTPotion.handle_response/1
       (elixir) lib/enum.ex:977: anonymous fn/3 in Enum.map/2
       (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
       (elixir) lib/enum.ex:977: Enum.map/2

我看到:url_parsing_failed,但我不明白为什么。当我尝试 Enum.map(fn(addr) -&gt; HTTPotion.get(base &lt;&gt; addr) 使用列表中的单个 url 路径时,它们都可以工作。

  • 我的语法错了吗?
  • 我是否遗漏了有关管道或 Enum 的工作原理的内容?
  • 我走对了吗?

根据 manukall 的回答,这里是有效的:

defmodule Scraper do
  def transform_url(url_or_path = "/" <> _, base), do: base <> url_or_path
  def transform_url(url, _base), do: url

  def start do
    base = "https://en.wikipedia.org"
    response = HTTPotion.get base <> "/wiki/Main_Page"
    html = response.body
    main_bg = Floki.find(html, ".MainPageBG")
    main_bg
      |> Floki.find("table tr li a")
      |> Floki.attribute("href")
      |> Enum.map(fn(url) -> OldRazor.transform_url(url, base) end)
      |> Enum.map(fn(url) -> HTTPotion.get(url) end)
  end
end

【问题讨论】:

    标签: url enums web-scraping html-parsing elixir


    【解决方案1】:

    如果您再次仔细查看网址列表,您会注意到其中有一个绝对网址:“https://lists.wikimedia.org/mailman/listinfo/daily-article-lwow”。这不适用于HTTPotion.get(base &lt;&gt; addr),因为它最终会请求像“https://en.wikipedia.orghttps://lists.wikimedia.org/mailman/listinfo/daily-article-lwow”这样的网址。

    解决这个问题的一种方法是编写另一个函数transform_url,该函数检查值是否以/ 开头,然后才在其前面添加基本网址:

      def transform_url(url_or_path = "/" <> _, base), do: base <> url_or_path
      def transform_url(url, _base), do: url
    

    然后你会用它作为

      ...
      |> Enum.map(fn(url) -> HTTPoison.get!(transform_url((url)) end)
    

    【讨论】:

    • 我实际上运行了您的代码,但在 HTTPotion.get! 部分之前将IO.inspect(url) 放入匿名函数中。所以最后打印的 URL 是损坏的。
    • 经过一些调整,效果很好!谢谢。我真的不明白(url_or_path = "/" &lt;&gt; _, base) 做了什么。
    • 太棒了! url_or_path = "/" &lt;&gt; _ 匹配以 / 开头的二进制文件,然后将整个二进制文件绑定到 url_or_path(如果匹配)。所以基本上整个方法会做这样的事情:如果第一个参数以“/”开头,则将该参数绑定到url_or_path,添加base 并返回它。否则只返回第一个参数。
    • 哦,太棒了。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多