JavaScript能读取任何网页的源代码吗?

84

我正在进行屏幕抓取(screen scraping)的工作,想要获取特定页面的源代码。

如何使用JavaScript实现这一点?请帮帮我。


1
这里有一个类似的页面,你可以在那里找到答案,因为它解决了我获取HTML页面源代码的问题。https://dev59.com/83M_5IYBdhLWcg3wdy7z - Asim Sajjad
7
为什么在问题是关于 JavaScript 的时候你还提到了 PHP 呢?下面的答案展示了如何用 JavaScript 来实现这个功能。 - corgrath
1
要获取链接的源代码,您可能需要使用 $.ajax 来处理外部链接。这里有一个解决方案 - https://dev59.com/smbWa4cB1Zd3GeqPY6ns#18447625 - otaxige_aol
2
所有的答案都是基于jQuery的,没有一个是原生Javascript的。 - ILikeTacos
2
jQuery是原生JavaScript。它只是你可以从jquery.com而不是stackoverflow.com复制的JavaScript。 - Quentin
17个回答

113

开始的简单方法,尝试使用jQuery

$("#links").load("/Main_Page #jq-p-Getting-Started li");

更多内容请参见jQuery文档 另一种更有结构化的屏幕抓取方式是使用 YQL 或 Yahoo查询语言,它将以JSON或XML格式返回抓取的数据。
例如:
让我们来抓取stackoverflow.com网站。
select * from html where url="http://stackoverflow.com"

将会为您提供一个JSON数组(我选择了这个选项),如下所示

 "results": {
   "body": {
    "noscript": [
     {
      "div": {
       "id": "noscript-padding"
      }
     },
     {
      "div": {
       "id": "noscript-warning",
       "p": "Stack Overflow works best with JavaScript enabled"
      }
     }
    ],
    "div": [
     {
      "id": "notify-container"
     },
     {
      "div": [
       {
        "id": "header",
        "div": [
         {
          "id": "hlogo",
          "a": {
           "href": "/",
           "img": {
            "alt": "logo homepage",
            "height": "70",
            "src": "http://i.stackoverflow.com/Content/Img/stackoverflow-logo-250.png",
            "width": "250"
           }
……..

这样做的好处是你可以使用投影和where子句,从而将爬取到的数据结构化,并且只获取你需要的数据(最终通过网络传输的带宽更小)
例如:

select * from html where url="http://stackoverflow.com" and
      xpath='//div/h3/a'

会让你获得

 "results": {
   "a": [
    {
     "href": "/questions/414690/iphone-simulator-port-for-windows-closed",
     "title": "Duplicate: Is any Windows simulator available to test iPhone application? as a hobbyist who cannot afford a mac, i set up a toolchain kit locally on cygwin to compile objecti … ",
     "content": "iphone\n                simulator port for windows [closed]"
    },
    {
     "href": "/questions/680867/how-to-redirect-the-web-page-in-flex-application",
     "title": "I have a button control ....i need another web page to be redirected while clicking that button .... how to do that ? Thanks ",
     "content": "How\n                to redirect the web page in flex application ?"
    },
…..

现在我们只需要问题,执行以下操作:
select title from html where url="http://stackoverflow.com" and
      xpath='//div/h3/a'

请注意投影中的标题。
 "results": {
   "a": [
    {
     "title": "I don't want the function to be entered simultaneously by multiple threads, neither do I want it to be entered again when it has not returned yet. Is there any approach to achieve … "
    },
    {
     "title": "I'm certain I'm doing something really obviously stupid, but I've been trying to figure it out for a few hours now and nothing is jumping out at me. I'm using a ModelForm so I can … "
    },
    {
     "title": "when i am going through my project in IE only its showing errors A runtime error has occurred Do you wish to debug? Line 768 Error:Expected')' Is this is regarding any script er … "
    },
    {
     "title": "I have a java batch file consisting of 4 execution steps written for analyzing any Java application. In one of the steps, I'm adding few libs in classpath that are needed for my co … "
    },
    {
……

一旦您编写查询,它会为您生成一个URL。在我们的情况下,它是这样的:http://query.yahooapis.com/v1/public/yql?q=select%20title%20from%20html%20where%20url%3D%22http%3A%2F%2Fstackoverflow.com%22%20and%0A%20%20%20%20%20%20xpath%3D'%2F%2Fdiv%2Fh3%2Fa'%0A%20%20%20%20&format=json&callback=cbfunc。因此,您最终会像这样做:
var titleList = $.getJSON(theAboveUrl);

带着它并开始玩吧。

漂亮,不是吗?


5
太棒了,尤其是对于暗示贫穷人的解决方案在雅虎上消除了需要代理获取数据的需求。谢谢!我顺便修复了最后一个演示链接到query.yahooapis.com:它在URL编码中缺少了一个%符号。很酷,这仍然有效! - GitaarLAB
3
query.yahooapis已于2019年1月停用。看起来非常好用,可惜现在我们不能使用它了。在此处查看推文:https://twitter.com/ydn/status/1079785891558653952?ref_src=twsrc%5Etfw - mindoverflow

33

只要通过你的域上的代理抓取所需页面,就可以使用JavaScript:

<html>
<head>
<script src="/js/jquery-1.3.2.js"></script>
</head>
<body>
<script>
$.get("www.mydomain.com/?url=www.google.com", function(response) { 
    alert(response) 
});
</script>
</body>

5
为什么需要基于域的代理? - Ravindranath Akila
5
由于同源策略。 - Ferdi265
那真的很有趣。大概需要在服务器上安装一些代码才能实现这个功能吧? - S Meaden
@ejbytes:实际上我认为node.js有一些模块。我假设OP想要进行网页抓取。 - S Meaden
如果您不在同一域中,则会收到“来自原点'null'的请求已被CORS策略阻止:所请求的资源上不存在'Access-Control-Allow-Origin'标头。”的错误提示。 - Gerrit B

7

您可以简单地使用 XmlHttp(AJAX)来访问所需的URL,URL返回的HTML响应将在responseText属性中可用。如果不是同一域,您的用户将收到浏览器警报,显示类似于“此页面正在尝试访问不同的域。您是否要允许此操作?”的消息。


3
很遗憾,您不会收到任何警报,它只会阻止请求。 - Alex from Jitbit

7
你可以使用 Fetch API

const URL = 'https://www.sap.com/belgique/index.html';
fetch(URL)
.then(res => res.text())
.then(text => {
    console.log(text);
})
.catch(err => console.log(err));


5
作为一项安全措施,Javascript 无法读取来自不同域的文件。虽然可能有一些奇怪的解决方法,但我建议考虑使用其他语言完成此任务。

4

如果你一定需要使用JavaScript,你可以使用ajax请求加载页面源代码。

请注意,使用JavaScript时,你只能检索请求页面所在域下的页面。


3
使用 jQuery
<html>
<head>
<script src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.js" ></script>
</head>
<body>
<script>
$.get("www.google.com", function(response) { alert(response) });
</script>
</body>

10
您不能以这种方式请求您域名之外的页面,您必须通过代理来执行,例如:$.get('http://mydomain.com/?url=www.google.com')。 - karim79

3
我使用了ImportIO。如果你在他们那里注册账户(免费的),就可以请求任何网站的HTML内容。每年最多可以发出50,000个请求。我没有花时间寻找替代方案,但我相信一定有其他的解决方法。

在你的Javascript代码中,你只需要像这样发送一个GET请求:

var request = new XMLHttpRequest();

request.onreadystatechange = function() {
  jsontext = request.responseText;

  alert(jsontext);
}

request.open("GET", "https://extraction.import.io/query/extractor/THE_PUBLIC_LINK_THEY_GIVE_YOU?_apikey=YOUR_KEY&url=YOUR_URL", true);

request.send();

附注:我在研究同样问题时发现了这个问题,因此其他人可能会发现我的解决方案有用。
更新:我创建了一个新的工具,他们只允许我使用不到48小时就说我必须付款才能继续使用。现在如果你不付费,他们很快就会关闭你的项目。我使用NodeJS和一个叫NightmareJS的库制作了一个类似的服务。您可以查看他们的教程这里并创建自己的网络抓取工具。这相对容易。我还没有尝试将其设置为API以便进行请求等。

1
尽管有很多相反的评论,但我认为使用简单的JavaScript可以克服同源要求。
我并不声称以下内容是原创的,因为我相信我在很久以前在其他地方看到了类似的东西。
我只在Mac上用Safari测试过这个功能演示。
以下演示获取基础标签中的页面,并将其innerHTML移动到新窗口。我的脚本添加了html标签,但在大多数现代浏览器中,这可以通过使用outerHTML避免。
<html>
<head>
<base href='http://apod.nasa.gov/apod/'>
<title>test</title>
<style>
body { margin: 0 }
textarea { outline: none; padding: 2em; width: 100%; height: 100% }
</style>
</head>
<body onload="w=window.open('#'); x=document.getElementById('t'); a='<html>\n'; b='\n</html>'; setTimeout('x.innerHTML=a+w.document.documentElement.innerHTML+b; w.close()',2000)">
<textarea id=t></textarea>
</body>
</html>

8.0.3。除了一些错误(我没有记住)出现在控制台中,什么都没有发生。 - Quentin
仍然是8.0.3版本,如果你真的想让我重现测试用例:TypeError: undefined is not an object (evaluating 'w.document') - Quentin
你所做的最有可能的解释是,由于你使用的古老浏览器和非官方补丁程序的某种组合,你发现了一个安全漏洞。在大多数情况下,这并没有什么实际用途。 - Quentin
抱歉,我错过了你的8.0.3版本 - 在除Firefox之外的所有浏览器中都可以工作 - 我谷歌了你的错误并找到了三种可能性:弹出窗口未启用,有时需要延迟页面以填充弹出窗口,这是一个已记录的webkit错误 - 我最初的测试是在本地进行的,现在才注意到它在我的服务器上无法工作 - 你是在本地还是在服务器上测试的? - Neville Hillyer
我很抱歉一开始没有表述得更清楚 - 它可能仍然是一个有用的设备,它仍然展示了同源限制的异常情况 - 我将调查其在服务器上的可能使用情况 - 它在本地对你有效吗? - Neville Hillyer
显示剩余3条评论

1
你可以通过创建浏览器扩展程序或在Windows上将文件保存为.hta(HTML应用程序)来绕过同源策略。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接