python 获取网页的内容

1.安装pip

我的个人桌面系统用的linuxmint,系统默认没有安装pip,考虑到后面安装requests模块使用pip,所以我这里第一步先安装pip。

1

$ sudo apt install python-pip

安装成功,查看PIP版本:

1

$ pip -V

2.安装requests模块

这里我是通过pip方式进行安装:

1

$ pip install requests

运行import requests,如果没提示错误,那说明已经安装成功了!

检验是否安装成功

3.安装beautifulsoup4

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库。它能够通过你喜欢的转换器实现惯用的文档导航,查找、修改文档的方式。Beautiful Soup会帮你节省数小时甚至数天的工作时间。

1

$ sudo apt-get install python3-bs4

注:这里我使用的是python3的安装方式,如果你用的是python2,可以使用下面命令安装。

1

$ sudo pip install beautifulsoup4

4.requests模块浅析

1)发送请求

首先当然是要导入 Requests 模块:

1

>>> import requests

然后,获取目标抓取网页。这里我以下为例:

1

>>> r = requests.get('http://www.jb51.net/article/124421.htm')

这里返回一个名为 r 的响应对象。我们可以从这个对象中获取所有我们想要的信息。这里的get是http的响应方法,所以举一反三你也可以将其替换为put、delete、post、head。

2)传递URL参数

有时我们想为 URL 的查询字符串传递某种数据。如果你是手工构建 URL,那么数据会以键/值对的形式置于 URL 中,跟在一个问号的后面。例如, cnblogs.com/get?key=val。 Requests 允许你使用 params 关键字参数,以一个字符串字典来提供这些参数。

举例来说,当我们google搜索“python爬虫”关键词时,newwindow(新窗口打开)、q及oq(搜索关键词)等参数可以手工组成URL ,那么你可以使用如下代码:

1

2

3

>>> payload = {'newwindow': '1', 'q': 'python爬虫', 'oq': 'python爬虫'}

>>> r = requests.get("https://www.google.com/search", params=payload)

3)响应内容

通过r.text或r.content来获取页面响应内容。

1

2

3

4

5

>>> import requests

>>> r = requests.get('https://github.com/timeline.json')

>>> r.text

Requests 会自动解码来自服务器的内容。大多数 unicode 字符集都能被无缝地解码。这里补充一点r.text和r.content二者的区别,简单说:

resp.text返回的是Unicode型的数据;

resp.content返回的是bytes型也就是二进制的数据;

所以如果你想取文本,可以通过r.text,如果想取图片,文件,则可以通过r.content。

4)获取网页编码

1

2

3

4

5

>>> r = requests.get('http://www.cnblogs.com/')

>>> r.encoding

'utf-8'

5)获取响应状态码

我们可以检测响应状态码:

1

2

3

4

5

>>> r = requests.get('http://www.cnblogs.com/')

>>> r.status_code

200

5.案例演示

并且只抓取页面中文章标题和内容等有用信息。

演示环境

操作系统:linuxmint

python版本:python 3.5.2

使用模块:requests、beautifulsoup4

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

#!/usr/bin/env python

# -*- coding: utf-8 -*-

_author_ = 'GavinHsueh'

import requests

import bs4

#要抓取的目标页码地址

url = 'http://www.ranzhi.org/book/ranzhi/about-ranzhi-4.html'

#抓取页码内容,返回响应对象

response = requests.get(url)

#查看响应状态码

status_code = response.status_code

#使用BeautifulSoup解析代码,并锁定页码指定标签内容

content = bs4.BeautifulSoup(response.content.decode("utf-8"), "lxml")

element = content.find_all(id='book')

print(status_code)

print(element)

一下为获取网页上特定的内容的程序   没有注释  

import requests
from bs4 import BeautifulSoup
import bs4
import os
from time import sleep
url_list = []
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
def url_all():
    for page in range(1,401):
        url = 'http://blog.csdn.net/?ref=toolbar_logo&page='+str(page)
        url_list.append(url)
def essay_url(): #找到所有文章地址
    blog_urls = []
    for url in url_list:
        html = requests.get(url, headers=headers)
        html.encoding = html.apparent_encoding
        soup = BeautifulSoup(html.text, 'html.parser')
        for h3 in soup.find_all('h3'):
            blog_url = (h3('a')[0]['href'])
            blog_urls.append(blog_url)
    return blog_urls
def save_path():
    s_path = 'D:/blog/'
    if  not os.path.isdir(s_path):
        os.mkdir(s_path)
    else:
        pass
    return s_path
def save_essay(blog_urls,s_path): #找到所有文章标题,文章内容。
    for url in blog_urls:
        blog_html = requests.get(url, headers=headers)
        blog_html.encoding = blog_html.apparent_encoding
        soup = BeautifulSoup(blog_html.text, 'html.parser')
        try:
            for title in soup.find('span', {'class': 'link_title'}):
                if isinstance(title, bs4.element.Tag):
                    print('-----文章标题-----:', title.text)
                    blogname = title.text
                    blogname = blogname.replace("\n",'')
                    blogname = blogname.replace("\r",'')
                    blogname = blogname.replace(" ",'')
                    try:
                        file = open(s_path + str(blogname) + '.txt', 'w')
                        file.write(str(title.text))
                        file.close()
                    except BaseException as a:
                        print(a)

            for p in soup.find('div', {'class': 'article_content'}).children:
                if isinstance(p, bs4.element.Tag):
                    try:
                        file = open(s_path + str(blogname) + '.txt', 'a')
                        file.write(p.text)
                        file.close()
                    except BaseException as f:
                        print(f)
        except BaseException as b:
            print(b)
    print('---------------所有页面遍历完成----------------')
sleep(10)
url_all()
save_essay(essay_url(),save_path())
 

代码

读入网页加以解析抓取,需要用到的软件包是 requests_html 。我们此处并不需要这个软件包的全部功能,只读入其中的 HTMLSession 就可以。

from requests_html import HTMLSession

然后,我们建立一个会话(session),即让Python作为一个客户端,和远端服务器交谈。

session = HTMLSession()

前面说了,我们打算采集信息的网页,是《如何用《玉树芝兰》入门数据科学?》一文。

我们找到它的网址,存储到url变量名中。

url = 'https://www.jianshu.com/p/85f4624485b9'

下面的语句,利用 session 的 get 功能,把这个链接对应的网页整个儿取回来。

r = session.get(url)

网页里面都有什么内容呢?

我们告诉Python,请把服务器传回来的内容当作HTML文件类型处理。我不想要看HTML里面那些乱七八糟的格式描述符,只看文字部分。

于是我们执行:

print(r.html.text)

这就是获得的结果了:

我们心里有数了。取回来的网页信息是正确的,内容是完整的。

好了,我们来看看怎么趋近自己的目标吧。

我们先用简单粗暴的方法,尝试获得网页中包含的全部链接。

把返回的内容作为HTML文件类型,我们查看 links 属性:

r.html.links

这是返回的结果:

这么多链接啊!

很兴奋吧?

不过,你发现没有?这里许多链接,看似都不完全。例如第一条结果,只有:

'/'

这是什么东西?是不是链接抓取错误啊?

不是,这种看着不像链接的东西,叫做相对链接。它是某个链接,相对于我们采集的网页所在域名(https://www.jianshu.com)的路径。

这就好像我们在国内邮寄快递包裹,填单子的时候一般会写“XX省XX市……”,前面不需要加上国家名称。只有国际快递,才需要写上国名。

但是如果我们希望获得全部可以直接访问的链接,怎么办呢?

很容易,也只需要一条 Python 语句。

r.html.absolute_links

这里,我们要的是“绝对”链接,于是我们就会获得下面的结果:

这回看着是不是就舒服多了?

我们的任务已经完成了吧?链接不是都在这里吗?

链接确实都在这里了,可是跟我们的目标是不是有区别呢?

检查一下,确实有。

我们不光要找到链接,还得找到链接对应的描述文字呢,结果里包含吗?

没有。

结果列表中的链接,都是我们需要的吗?

不是。看长度,我们就能感觉出许多链接并不是文中描述其他数据科学文章的网址。

这种简单粗暴直接罗列HTML文件中所有链接的方法,对本任务行不通。

那么我们该怎么办?

我们得学会跟 Python 说清楚我们要找的东西。这是网页抓取的关键

想想看,如果你想让助手(人类)帮你做这事儿,怎么办?

你会告诉他:

“寻找正文中全部可以点击的蓝色文字链接,拷贝文字到Excel表格,然后右键复制对应的链接,也拷贝到Excel表格。每个链接在Excel占一行,文字和链接各占一个单元格。”

虽然这个操作执行起来麻烦,但是助手听懂后,就能帮你执行。

同样的描述,你试试说给电脑听……不好意思,它不理解。

因为你和助手看到的网页,是这个样子的。

电脑看到的网页,是这个样子的。

为了让你看得清楚源代码,浏览器还特意对不同类型的数据用了颜色区分,对行做了编号。

数据显示给电脑时,上述辅助可视功能是没有的。它只能看见一串串字符。

那可怎么办?

仔细观察,你会发现这些HTML源代码里面,文字、图片链接内容前后,都会有一些被尖括号括起来的部分,这就叫做“标记”。

所谓HTML,就是一种标记语言(超文本标记语言,HyperText Markup Language)。

标记的作用是什么?它可以把整个的文件分解出层次来。

(图片来源:https://goo.gl/kWCqS6

如同你要发送包裹给某个人,可以按照“省-市-区-街道-小区-门牌”这样的结构来写地址,快递员也可以根据这个地址找到收件人。

同样,我们对网页中某些特定内容感兴趣,可以依据这些标记的结构,顺藤摸瓜找出来。

这是不是意味着,你必须先学会HTML和CSS,才能进行网页内容抓取呢?

不是的,我们可以借助工具,帮你显著简化任务复杂度。

这个工具,Google Chrome浏览器自带。

我们在样例文章页面上,点击鼠标右键,在出现的菜单里面选择“检查”。

这时,屏幕下方就会出现一个分栏。

我们点击这个分栏左上角(上图红色标出)的按钮。然后把鼠标悬停在第一个文内链接(《玉树芝兰》)上面,点击一下。

此时,你会发现下方分栏里面,内容也发生了变化。这个链接对应的源代码被放在分栏区域正中,高亮显示。

确认该区域就是我们要找的链接和文字描述后,我们鼠标右键选择高亮区域,并且在弹出的菜单中,选择 Copy -> Copy selector。

找一个文本编辑器,执行粘贴,就可以看见我们究竟复制下来了什么内容。

body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a

这一长串的标记,为电脑指出了:请你先找到 body 标记,进入它管辖的这个区域后去找 div.note 标记,然后找……最后找到 a 标记,这里就是要找的内容了。

回到咱们的 Jupyter Notebook 中,用刚才获得的标记路径,定义变量sel。

sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a'

我们让 Python 从返回内容中,查找 sel 对应的位置,把结果存到 results 变量中。

results = r.html.find(sel)

我们看看 results 里面都有什么。

results

这是结果:

[<Element 'a' href='https://www.jianshu.com/nb/130182' target='_blank'>]

results 是个列表,只包含一项。这一项包含一个网址,就是我们要找的第一个链接(《玉树芝兰》)对应的网址。

可是文字描述“《玉树芝兰》”哪里去了?

别着急,我们让 Python 显示 results 结果数据对应的文本。

results[0].text

这是输出结果:

'玉树芝兰'

我们把链接也提取出来:

results[0].absolute_links

显示的结果却是一个集合。

{'https://www.jianshu.com/nb/130182'}

我们不想要集合,只想要其中的链接字符串。所以我们先把它转换成列表,然后从中提取第一项,即网址链接。

list(results[0].absolute_links)[0]

这次,终于获得我们想要的结果了:

'https://www.jianshu.com/nb/130182'

有了处理这第一个链接的经验,你信心大增,是吧?

其他链接,也无非是找到标记路径,然后照猫画虎嘛。

可是,如果每找一个链接,都需要手动输入上面这若干条语句,那也太麻烦了。

这里就是编程的技巧了。重复逐条运行的语句,如果工作顺利,我们就要尝试把它们归并起来,做个简单的函数。

对这个函数,只需给定一个选择路径(sel),它就把找到的所有描述文本和链接路径都返回给我们。

def get_text_link_from_sel(sel):
    mylist = []
    try:
        results = r.html.find(sel)
        for result in results:
            mytext = result.text
            mylink = list(result.absolute_links)[0]
            mylist.append((mytext, mylink))
        return mylist
    except:
        return None

我们测试一下这个函数。

还是用刚才的标记路径(sel)不变,试试看。

print(get_text_link_from_sel(sel))

输出结果如下:

[('玉树芝兰', 'https://www.jianshu.com/nb/130182')]

没问题,对吧?

好,我们试试看第二个链接。

我们还是用刚才的方法,使用下面分栏左上角的按钮点击第二个链接。

下方出现的高亮内容就发生了变化:

我们还是用鼠标右键点击高亮部分,拷贝出 selector。

然后我们直接把获得的标记路径写到 Jupyter Notebook 里面。

sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a'

用我们刚才编制的函数,看看输出结果是什么?

print(get_text_link_from_sel(sel))

输出如下:

[('如何用Python做词云?', 'https://www.jianshu.com/p/e4b24a734ccc')]

检验完毕,函数没有问题。

下一步做什么?

你还打算去找第三个链接,仿照刚才的方法做?

那你还不如全文手动摘取信息算了,更省事儿一些。

我们要想办法把这个过程自动化

对比一下刚刚两次我们找到的标记路径:

body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a

以及:

body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a

发现什么规律没有?

对,路径上其他的标记全都是一样的,唯独倒数第二个标记("p")后冒号后内容有区别。

这就是我们自动化的关键了。

上述两个标记路径里面,因为指定了在第几个“子”(nth-child)文本段(paragraph,也就是"p"代表的含义)去找"a"这个标记,因此只返回来单一结果。

如果我们不限定"p"的具体位置信息呢?

我们试试看,这次保留标记路径里面其他全部信息,只修改"p"这一点。

sel = 'body > div.note > div.post > div.article > div.show-content > div > p > a'

再次运行我们的函数:

print(get_text_link_from_sel(sel))

这是输出结果:

好了,我们要找的内容,全都在这儿了。

但是,我们的工作还没完。

我们还得把采集到的信息输出到Excel中保存起来。

还记得我们常用的数据框工具 Pandas 吗?又该让它大显神通了。

import pandas as pd

只需要这一行命令,我们就能把刚才的列表变成数据框:

df = pd.DataFrame(get_text_link_from_sel(sel))

让我们看看数据框内容:

df

内容没问题,不过我们对表头不大满意,得更换为更有意义的列名称:

df.columns = ['text', 'link']

再看看数据框内容:

df

好了,下面就可以把抓取的内容输出到Excel中了。

Pandas内置的命令,就可以把数据框变成csv格式,这种格式可以用Excel直接打开查看。

df.to_csv('output.csv', encoding='gbk', index=False)

注意这里需要指定encoding(编码)为gbk,否则默认的utf-8编码在Excel中查看的时候,有可能是乱码。

我们看看最终生成的csv文件吧。

很有成就感,是不是?

小结

本文为你展示了用Python自动网页抓取的基础技能。希望阅读并动手实践后,你能掌握以下知识点:

  • 网页抓取与网络爬虫之间的联系与区别;
  • 如何用 pipenv 快速构建指定的 Python 开发环境,自动安装好依赖软件包;
  • 如何用 Google Chrome 的内置检查功能,快速定位感兴趣内容的标记路径;
  • 如何用 requests-html 包来解析网页,查询获得需要的内容元素;
  • 如何用 Pandas 数据框工具整理数据,并且输出到 Excel。

或许,你觉得这篇文章过于浅白,不能满足你的要求。

文中只展示了如何从一个网页抓取信息,可你要处理的网页成千上万啊。

别着急。

本质上说,抓取一个网页,和抓取10000个网页,在流程上是一样的。

而且,从咱们的例子里,你是不是已经尝试了抓取链接?

有了链接作为基础,你就可以滚雪球,让Python爬虫“爬”到解析出来的链接上,做进一步的处理。

将来,你可能还要应对实践场景中的一些棘手问题:

  • 如何把抓取的功能扩展到某一范内内的所有网页?
  • 如何爬取Javascript动态网页?
  • 假设你爬取的网站对每个IP的访问频率做出限定,怎么办?
  • ……

这些问题的解决办法,我希望在今后的教程里面,一一和你分享。

需要注意的是,网络爬虫抓取数据,虽然功能强大,但学习与实践起来有一定门槛。

当你面临数据获取任务时,应该先检查一下这个清单:

  • 有没有别人已经整理好的数据集合可以直接下载?
  • 网站有没有对你需要的数据提供API访问与获取方式?
  • 有没有人针对你的需求,编好了定制爬虫,供你直接调用?

如果答案是都没有,才需要你自己编写脚本,调动爬虫来抓取。

为了巩固学习的知识,请你换一个其他网页,以咱们的代码作为基础修改后,抓取其中你感兴趣的内容。

如果能把你抓取的过程记录下来,在评论区将记录链接分享给大家,就更好了。

因为刻意练习是掌握实践技能的最好方式,而教是最好的学

祝顺利!

思考

本文主要内容讲解完毕。

这里给你提一个疑问,供你思考:

我们解析并且存储的链接,其实是有重复的:

这并不是我们的代码有误,而是在《如何用《玉树芝兰》入门数据科学?》一文里,本来就多次引用过一些文章,所以重复的链接就都被抓取出来了



作者:王树义
链接:https://www.jianshu.com/p/ba02079ecd2f
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


 

猜你喜欢

转载自blog.csdn.net/qq_36958104/article/details/83824181