python find函数_Python爬虫入门到入职02:编写第一个爬虫程序

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

本章知识点:

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

python基础学习

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

  • 我是零基础:请先学习python基础教程,强烈推荐:廖雪峰的python教程。
    • 重点学习章节:python基础函数高级特性模块面相对象编程错误、调试和测试IO编程
    • 初步了解章节:进程和线程正则表达式常用内建模块常用第三方模块virtualenv网络编程Web开发。这些内容十分重要,但初期涉及不多。
    • 暂时跳过章节:面向对象高级编程函数式编程图形界面电子邮件访问数据库异步IO实战,这些内容会在Python爬虫-高级进阶系列教程涉及。
    • 学习时间: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源码:

7582f884247e0ae63e4cb15bb83810b7.png

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

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

使用requests进行抓取

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

import requests

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

执行程序,报错:

dba1ca428a61a04a307598f23ad6f63a.png

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

8e4d308be4ba81df319aa1d87eb6c884.png

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

  • 高于10.0版本,执行命令:pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
  • 低于10.0版本,参考教程:设置pip安装源为国内清华大学镜像

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

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

6ff92b66be0020fc15bc0b533b404138.png

源码解析

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

bf6181cbeae6af3fa595720744ca1281.png

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

6a9e16f9d9aa1a0d2b302aa6c7fb7dd2.png

修改代码:

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属性的值)

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

60492cc8db42f99cf52b5a41ecca5877.png

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

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)

第一页采集成功:

7a7f6211bfc1aa609ff891658548bc3b.png

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

879a99fe62342e874fb84bd28c148bf0.png

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

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)

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

4756430fbf518ea3514b74933e2c53ff.png

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

  1. 混进了奇怪的“n”字符:n是换行字符,对于字符串首位的空白字符(n,t,空格),可以使用strip()函数剔除。
  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)

看看修改后的采集结果:

8d4152496e90efe855e1ebc32389c4cb.png

图片有了,文章开头也没有含换行符了,感觉自己太牛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:全量抓取

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值