美文网首页框架
Python爬虫入门到入职02:编写第一个爬虫程序

Python爬虫入门到入职02:编写第一个爬虫程序

作者: sumriser | 来源:发表于2019-02-22 10:09 被阅读51次

    教程中的项目请跟着在pycharm中写一遍,注意查看注释内容。推荐的课外练习请自行完成,完成后再查看参考代码。

    本章知识点:

    • python基础
    • 使用requests库发送请求
    • 使用beautifulsoup解析网页源码

    python基础学习

    本教程爬虫开发需要python语言基础,请根据自身情况选择:

    • 我是零基础:请先学习python基础教程,强烈推荐:廖雪峰的python教程

      1. 重点学习章节:python基础函数高级特性模块面相对象编程错误、调试和测试IO编程
      2. 初步了解章节:进程和线程正则表达式常用内建模块常用第三方模块virtualenv网络编程Web开发。这些内容十分重要,但初期涉及不多,需要时我会提醒读大家回顾。
      3. 暂时跳过章节:面向对象高级编程函数式编程图形界面电子邮件访问数据库异步IO实战,这些内容会在Python爬虫-高级进阶系列教程涉及。
      4. 学习时间:2~3天
    • 我有编程基础:直接开始爬虫教程。

    第一个爬虫程序:游戏葡萄

    创建一个game_grape.py文件,写入代码:

    from urllib import request  # 导入urllib中的request模块
    
    response= request.urlopen('http://youxiputao.com/')  # 用urlopen()函数执行一次请求,地址是游戏葡萄的首页,并把返回对象赋值给response变量。
    html = response.read().decode()  # 从response中读取结果,解码成str类型的字符串,就是我们的html源码。
    print(html)  # 在控制台打印html源码。
    

    执行程序,控制台打印出首页的html源码:

    程序运行成功,超级简单有木有!

    其实爬虫的本质就是:发起请求获得响应解析结果。企业中千万级爬虫、分布式爬虫都是在这个基础上抓取得更快、存储得更多、突破各种反爬虫措施而已。

    使用requests进行抓取

    使用python内置的urllib包虽然可以进行抓取,但使用非常不方便,特别是面对复杂的网络请求时,使用起来会力不从心。我们来尝试用第三方网络请求工具requests重写爬虫。

    import requests
    
    response = requests.get('http://youxiputao.com/')
    print(response.text)
    

    执行程序,报错:

    requests是第三方库,需要安装后使用:按住Win+R,输入powershell(Win10以下输入cmd),按回车键,输入命令pip install requests,如图:

    第一次安装需要下载,如果速度太慢安装失败,可以把下载源更换为清华大学镜像:输入pip -V命令,查看pip版本:

    换源之后重新安装requests,出现Successfully installed xxx即为安装成功。

    再次执行代码,成功打印出html源码:

    源码解析

    我们想解析网页源码,获得新闻标题和链接,使用第三方库BeautifulSoup解析工具:pip install bs4。打开Chrome浏览器,右键点击新闻标题,点击检查,查看新闻标题的位置:

    根据源码位置找到标题,如图:

    修改代码:

    import requests
    from bs4 import BeautifulSoup
    
    response = requests.get('http://youxiputao.com/')  # 发起网络请求,获得响应
    soup = BeautifulSoup(response.text, 'html.parser')  # 使用BeautifulSoup解析,查找我们想要的内容。html.parser是系统内置的解析方案,可以不填但是会报警告(warning)
    ul_node = soup.find('ul', {'class': 'news-list'})  # 查找class值为news-list的ul标签,
    li_node = ul_node.find('li')  # 在ul标签下查找第一个li标签
    h4_node = li_node.find('h4')  # 在li标签下查找h4标签
    a_node = h4_node.find('a')  # 查找a标签
    print(a_node.text)  # 打印出a标签内容
    print(a_node['href'])  # 打印出a标签链接(href属性的值)
    

    成功解析出第一条新闻标题和链接:

    咦,情况不对,这个链接怎么看起来很别扭?而且只拿到第一条新闻,剩下的怎么办呢?继续修改代码:

    import requests
    from bs4 import BeautifulSoup
    from urllib.parse import urljoin  #从urllib库导入urljoin()函数,用于url拼接
    
    response = requests.get('http://youxiputao.com/')
    soup = BeautifulSoup(response.text, 'html.parser')
    for li_node in soup.find('ul', {'class': 'news-list'}).find_all('li'):  # 使用find_all()找出所有li标签,在for循环中解析每个<a>标签
        a_node = li_node.find('h4').find('a')  # find()函数返回标签,可以连续查找;find_all()返回的是符合条件的所有标签的数组,需要循环遍历
        title = a_node.text
        href = a_node['href']
        url = urljoin(response.url, href)  # response.url是响应的地址,不一定是原始请求地址哦,因为网站可能会把对请求做重定向
        print(url, title)
    

    第一页采集成功:

    拿到了详情页面的链接,我们继续发送请求,采集详细数据。随便点开一个新闻页面,在chrome中按F12键查看页面结构:我们需要标题、时间、正文三部分,并把它们放在一个字典里来表示这条数据。

    在刚才代码后面继续添加:

    import requests
    from bs4 import BeautifulSoup
    from urllib.parse import urljoin  # 从urllib库导入urljoin()函数,用于url拼接
    
    response = requests.get('http://youxiputao.com/')
    soup = BeautifulSoup(response.text, 'html.parser')
    for li_node in soup.find('ul', {'class': 'news-list'}).find_all('li'):  # 使用find_all()找出所有li标签,在for循环中解析每个<a>标签
        a_node = li_node.find('h4').find('a')  # find()函数返回标签,可以连续查找;find_all()返回的是符合条件的所有标签的数组,需要循环遍历
        href = a_node['href']
        url = urljoin(response.url, href)  # response.url是响应的地址,不一定是原始请求地址哦,因为网站可能会把对请求做重定向
    
        response_detail = requests.get(url)  # 用response_detail 来区别之前的response
        soup_detail = BeautifulSoup(response_detail.text, 'html.parser')  # 继续解析
        title = soup_detail.find('h2', class_='title').text  # 可以用class_参数定位标签,仅限包含class、id等字段的标签,字典写法更通用
        publish_time = soup_detail.find('div', {'class': 'pull-left'}).text  # 直接获取div标签的“text”属性,包含div标签下所有子标签的文本
        article = soup_detail.find('div', {'class': 'info-box col-sm-12'}).text
        data = {'title': title, 'publish_time': publish_time, 'article': article}
        print(data)
    

    执行程序,采集结果如下:

    查看结果,发现有两个问题:

    1. 混进了奇怪的“\n”字符:\n是换行字符,对于字符串首位的空白字符(\n,\t,空格),可以使用trip()函数剔除。
    2. 图片去哪儿了:真实的爬虫项目通常不会采集图片,原因很简单:大型网站上千万数据,每条数据几张图片,再多硬盘都装不下。企业中对图片常用的处理方案:保存图片链接

    继续修改刚才的代码:

    import requests
    from bs4 import BeautifulSoup
    from urllib.parse import urljoin
    
    response = requests.get('http://youxiputao.com/')
    soup = BeautifulSoup(response.text, 'html.parser')
    for li_node in soup.find('ul', {'class': 'news-list'}).find_all('li'):
        a_node = li_node.find('h4').find('a')
        href = a_node['href']
        url = urljoin(response.url, href)
    
        response_detail = requests.get(url)
        soup_detail = BeautifulSoup(response_detail.text, 'html.parser')
        title = soup_detail.find('h2', class_='title').text
        publish_time = soup_detail.find('div', {'class': 'pull-left'}).text.strip()
        article = soup_detail.find('div', {'class': 'info-detail'}).text.strip()  # strip()函数可以过滤字符串首尾的空白字符
        images = []  # 用于存放文章中的图片
        for img_node in soup_detail.find('div', {'class': 'info-detail'}).find_all('img'):  # 找到文章主体节点,继续找到所有<img>图片标签
            img_url = img_node['src']
            images.append(img_url)
        cover = soup_detail.find('div', class_='cover').find('img')['src']  # 封面图片
        images.append(cover)
        data = {'title': title, 'publish_time': publish_time, 'images': images, 'article': article}
        print(data)
    

    看看修改后的采集结果:

    图片有了,文章开头也没有含换行符了,感觉自己太牛X了!仔细查看字段内容,发现文章中间仍然包含\n,一般来说\n字符可以用来标识段落,不用处理。如果必须处理的话可以使用replace()函数:

    article = article.replace('\n', '')  # 使用“空字符”来替换“空白字符\n”,并重新赋值给article变量
    

    最后,我们用面向对象的方式改写程序,完整代码如下:

    import requests
    from bs4 import BeautifulSoup
    from urllib.parse import urljoin
    
    
    class GameGrapeSpider:  # 声明一个叫“GameGrapeSpider(游戏葡萄爬虫)”的类
        def start(self):  # 爬虫的启动函数
            response = requests.get('http://youxiputao.com/')
            soup = BeautifulSoup(response.text, 'html.parser')
            for li_node in soup.find('ul', {'class': 'news-list'}).find_all('li'):
                a_node = li_node.find('h4').find('a')
                href = a_node['href']
                url = urljoin(response.url, href)
    
                response_detail = requests.get(url)
                soup_detail = BeautifulSoup(response_detail.text, 'html.parser')
                title = soup_detail.find('h2', class_='title').text
                publish_time = soup_detail.find('div', {'class': 'pull-left'}).text.strip()
                article = soup_detail.find('div', {'class': 'info-detail'}).text.strip()
                images = []  # 用于存放文章中的图片
                for img_node in soup_detail.find('div', {'class': 'info-detail'}).find_all('img'):
                    img_url = img_node['src']
                    images.append(img_url)
                cover = soup_detail.find('div', class_='cover').find('img')['src']  # 封面图片
                images.append(cover)
                data = {'title': title, 'publish_time': publish_time, 'images': images, 'article': article}
                print(data)
    
    
    if __name__ == '__main__':  # 程序入口,直接运行game_grape.py才执行,被导入时不会执行
        spider = GameGrapeSpider()  # 创建一个对象
        spider.start()  # 启动爬虫
    

    课外练习:抓取以下网站的首页内容

    1. 3DM-新闻频道:标题、时间、来源、作者、编辑、文章、图片字段。
    2. 天天美剧-排行榜:名字、排名、海报(链接)、更新日、状态、分类、最后更新、回归日期、倒计时等字段。

    练习答案:
    Github地址


    下一章 >> Python爬虫入门到入职03:全量抓取

    相关文章

      网友评论

        本文标题:Python爬虫入门到入职02:编写第一个爬虫程序

        本文链接:https://www.haomeiwen.com/subject/sewlyqtx.html