【问题标题】:Ruby - Error when trying to parse HTML page using JSON.load(), error 795 or 757Ruby - 尝试使用 JSON.load() 解析 HTML 页面时出错,错误 795 或 757
【发布时间】:2016-06-27 13:32:11
【问题描述】:

我正在尝试使用 JSON.load() 解析页面,但不幸的是,我总是遇到相同的错误 JSON::ParserError: 795: unexpected token at '{"...

当我不执行循环时,它实际上可以工作。如果我为 {page} 设置一个常量,比如说 1,它确实有效。在循环中,它失败了。

我认为,由于两个“{”而出现错误,因为我得到了两个哈希,一个用于第 1 页,一个用于第 2 页。我说的对吗?

这是我的代码:

@dl_page = []
(1..2).each do |page|
  puts "Loading page #{page}..."
  @dl_page << [ `(curl --url "https://angel.co/video?page=#{page}&sort=joined" -H "X-Requested-With: XMLHttpRequest")` ]
  @result = "#{@dl_page.join}"
end

html = JSON.load(@result)["html"]

非常感谢您的帮助!

【问题讨论】:

  • 为什么不使用 Ruby 内置的 net/http 库或众多 HTTP 请求 gem 之一(如 HTTParty,它将自动处理 JSON 响应)而不是对 curl 进行系统调用?跨度>

标签: arrays ruby json hash


【解决方案1】:

出了什么问题

首先,让我们看看您的代码中发生了什么。这是你的循环:

@dl_page = []
(1..2).each do |page|
  @dl_page << [ `(curl --url "https://angel.co/video?page=#{page}&sort=joined" -H "X-Requested-With: XMLHttpRequest")` ]
  @result = "#{@dl_page.join}"
end

假设您的 curl 命令第一次返回一个有效的 JSON 对象,例如 {"html":"foo"},第二次返回 {"html":"bar"}。让我们展开你的循环:

@dl_page = []

# First iteration

@dl_page << [ '{"html":"foo"}' ]
# => [ [ '{"html":"foo"}' ] ]

@result = @dl_page.join
# => '{"html":"foo"}'

请注意,由于您使用 &lt;&lt; 运算符将数组 ([ '{"html":"foo"}' ]) 附加到 @dl_page,因此 @dl_page 现在是一个具有单个元素的数组,它本身就是一个具有单个元素的数组。这可能不是你想要的。您可能想要的只是将 JSON 字符串本身附加到数组中,即@dl_page &lt;&lt; '{"html":"foo"}'

另请注意,我将"#{@dl_page.join}" 替换为@dl_page.join"#{foo}" 从来没有必要。 #{...} 用于字符串插值,这里没有插值,Array#join 返回一个字符串,所以上面完全等价于@result = @dl_page.join

docs for Array#join 有点模糊,但事实证明[ [ '{"html":"foo"}' ] ].join 只是返回'{"html":"foo"}'

正如您所发现的,如果您在此停止并使用JSON.load,它会起作用,因为@result 是一个有效的 JSON 对象。让我们找出为什么在第二次迭代之后不再是这种情况:

# Second iteration

@dl_page << [ '{"html":"bar"}' ]
# => [ [ '{"html":"foo"}' ], [ '{"html":"bar"}' ] ]

@result = @dl_page.join
# => '{"html":"foo"}{"html":"bar"}'

现在@dl_page 是一个包含两个元素的数组,每个元素本身就是一个包含一个元素的数组。当您调用 @dl_page.join 时,结果是两个 JSON 对象相互混合在一起。显然这是无效的,所以JSON.load 失败了。

如何解决

您有一些表面问题,例如将数组附加到 @dl_page 而不仅仅是单个元素,以及在循环内执行 @result = ... 而实际上您只需要在循环之后执行一次。这些都是值得解决的问题。

然而,这里真正的问题是你试图将两个 JSON 字符串转换为一个 JSON 字符串然后解析它,而你应该分别解析每个 JSON 字符串:

@result = []

(1..2).each do |page|
  json = `curl --url "https://angel.co/video?page=#{page}&sort=joined" -H "X-Requested-With: XMLHttpRequest"`
  @result << JSON.parse(json)
end

再次假设curl系统调用第一次返回{"html":"foo"},第二次返回{"html":"bar"},现在@result的值将是一个包含两个元素的数组,每个元素都是一个Ruby哈希:

p @result
# => [ { "html" => "foo" }, { "html" => "bar" } ]

如果您只想要 "html" 键的值,您可以这样做:

@result = []
(1..2).each do |page|
  json = `(curl --url "https://angel.co/video?page=#{page}&sort=joined" -H "X-Requested-With: XMLHttpRequest")`
  @result << JSON.parse(json)["html"]
end

现在@result 的值将是一个包含两个字符串的数组:

p @result
# => ["foo", "bar"]

不过,我们仍然可以做得更好。在 Ruby 中,通常情况下,如果您在循环外创建一个空对象,然后在循环内向该对象添加内容,您应该改用 Enumerable#mapEnumerable#reduce

@result = (1..2).map do |page|
  json = `curl --url "https://angel.co/video?page=#{page}&sort=joined" -H "X-Requested-With: XMLHttpRequest"`
  JSON.parse(json)["html"]
end

p @result
# => ["foo", "bar"]

【讨论】:

  • 我不知道该说什么@Jordan,你的评论太棒了,对我很有帮助,因为我是 Ruby 编程的新手。再次感谢您帮助像我这样的人成为开发人员(有一天):)
  • 每个人都曾经是新手。很高兴为您提供帮助!
【解决方案2】:

在您的代码中,@result 看起来像:"{"html": "&lt;...&gt;"}{"html": "&lt;...&gt;"}"。它不是有效的 JSON。

如果您需要连接两个请求中的 html:

@dl_page = (1..2).map do |page|
  puts "Loading page #{page}..."
  `(curl --url "https://angel.co/video?page=#{page}&sort=joined" -H "X-Requested-With: XMLHttpRequest")`
end

@result = "[#{@dl_page.join(',')}]"

html = JSON.load(@result)
# => [{"html": "<...>"}, {"html": "<...>"}]

【讨论】:

  • 嗨@Sergii K,感谢您的留言。我理解那部分,但我不知道如何解决这个问题并将其转换为有效的 JSON。有什么想法吗?
  • 谢谢@Sergii K,我用你的代码想出了一些办法:)谢谢!
猜你喜欢
  • 2017-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-19
相关资源
最近更新 更多