博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
记录一次爬取某昵称网站的爬虫
阅读量:4633 次
发布时间:2019-06-09

本文共 5353 字,大约阅读时间需要 17 分钟。

同学跑去实习了...然后工作的时候要她用python写一个爬虫,爬取一万个可以用的用户昵称。(为什么他们都能找到工作啊QAQ)

然后,她找到了我...然后在我动笔的时候,发现之前写过的爬虫基本上忘完了...无奈下只好对着以前写的项目,重新找了下文章,现在写一篇文章重新集合下之前零散的知识点。

我这里写的内容只是针对自己的需求写的,如果想要彻底了解BeautifulSoup的用法的话,可以参考下这篇文章: () => (看完这文章我都想敲一下Java8新增的lambda表达式了...真的超级炫酷的)

言归正传,开始吧。


python的爬虫,可能会用到两个包(过去是这样的),一个是BeautifulSoup,一个是etree

我用我的理解简单说说这两个包吧(这里的话十有八九别信...)

BeautifulSoup:

  • 一个抓取页面的插件,能够页面抓取出来,能对页面数据做简单的筛选
  • 引入方法: from bs4 import BeautifulSoup
  • 安装方法: pip install bs4

etree:

  • 在3.7(应该是3.7)版本以前,都是特别好用的,因为里面可以用XPath直接锁定DOM元素,但是在未来的更新中,它对XPath的兼容性并不好,所以干脆砍掉了。注:在谷歌浏览器里可以直接复制出页面的XPath,所以个人感觉没有必要去记XPath语法,毕竟我们可不是因为玩爬虫而玩python的,要知道,python可是因为人工智能而一鸣惊人。
  • 引入方式: from lxml import etree(这个词应该是Element Tree)
  • 安装方式: pip install lxml
  • 据说在4.1.1版本里可以通过from lxml.html import etree使用etree
  • 想要使用的话,可以用pip指定版本安装,安装过去的版本使用

因为我目前不是很想在这爬虫上折腾,所以这次就用了BeautifulSoup写完了本次的爬虫,这里就不对etree的用法做介绍了,想了解的话根据自己安装的lxml版本换一篇文章,就不浪费时间了

先来说说BeautifulSoup的四种数据类型,为了以后做铺垫:

  1. Tag
  2. NavigatableString
  3. BeautifulSoup
  4. Comment

Tag => 就是页面里的标签;

NavigableString => 如果你想获取Tag中的文字,你可以用TagName.string获取里面的内容,获取的值便是NavigableString类型的对象;
BeautifulSoup => 它是个功能更多的对象,你可以使用更多的方式获取子类的对象,获取方式很简单,举个栗子:

soup = BeautifulSoup(你就想象这里有很长很长很长的html代码吧, 'lxml')  # 获取页面文档 这是个BeautifulSoup类型的数据# 如果愿意的话,可以用 print(type(soup)) 检查下 soup 的数据类型,我没有测试item = soup.find_all(name='a', attrs={'class': 'hover', 'target': '_self'})  # 这里用的 find_all 方法,意思就是找到之前获取的文档对象(soup)下的所有满足的对象

Comment => 这个属性比较特殊了,它可以找到所有的注释内容...没用过

对于BeautifulSoup对象有很多属性可以用,比如说:

  • 靠标签名查找 => soup.select('div')
  • 靠选择器查找 => soup.select('.top-bar')
  • 靠id查找 => soup.select('#app')
  • 组合的 => soup.select('#app .top-bar')
  • 子类的 => soup.select('#app > .top-bar')
  • 具体 => soup.select('div[data="NickName" class="name"]') # 这里可以了解下Emmet语法,挺像的...方括号里的是属性,大括号里的是内容,中间不能有空格。

参考的文章 =>

基础东西讲完了...后面的感觉完全不用看了,如果你闲着没事干的话

正式开始上代码

靶机 (用靶机这个词是不是感觉特别装逼啊)

http://www.oicq88.com

思路

  1. 获取到页面元素

  2. 检查下文档页面后,发现它把昵称分成了很多类别,一共五十多个,点进去之后,会进入子域名,使用字符串拼接直接访问内部链接即可

  3. 每一个类别里有很多页数据,我们需要优先知道页面总数,才可以去遍历,至少不会出现角标越界的状况,无意间发现,在页面后面拼接的数字超出是不会报错的,并且能看到的页面是该网站的最后一页(我在说什么啊) | 其实还有个思路,爬完一页之后判断是否还有下一页,如果有,则继续向后遍历,没有则退出循环。

  4. 利用BeautifulSoup获取到所有的昵称内容(为什么不能前后台分离用,前台向后台请求json数据,这样我就能直接拿到所有的昵称了...也不知道这个是伪静态还是静态)

  5. IO流,文件的写入

大纲

拿到拿到页面元素

拿页面元素,很简单的

response = requests.get(url='http://www.oicq88.com')

不过我定义了个函数的说...

import requests  # 别忘了导包,报错的话装包 pip install requests,pip版本过低的话更新下,会有提示的# 模拟用户从浏览器登录def get_html(url):    headers = {        'User-Agent': 'Mozilla/5.0(Macintosh; Intel Mac OS X 10_11_4)\        AppleWebKit/537.36(KHTML, like Gecko) Chrome/52 .0.2743. 116 Safari/537.36'    }  # 模拟浏览器访问    response = requests.get(url, headers=headers)  # 请求访问网站    html = response.content.decode()  # 获取网页源码 因为编码问题,我们看不懂机器的语言,所以需要先解码    return html

然后为了方便,我在函数下面加了点全局使用的属性

base_url = 'http://www.oicq88.com'  # 基本域名file_name = 'result.txt'  # 输出的文件名currentItem = 0  # 当前的项目数,只是为了记录反馈用params = []

拼接得到所有昵称类型

这里说下find和find_all的区别,find指找到满足条件的第一条,而find_all是找到满足条件的所有条目。

举个栗子:

params = [ "item1", "item2", "item3" ]都使用(name='a', attrs={'class': 'A'})查找的情况下find相当于找到 "item1"find_all相当于找到 ["item1", "item2"]

然后,上代码

# 找到所有的子项目for name in soup.find_all(name='a'):  # 拿到所有 a 标签    child_path = name.get('href')  # 拿到 href 属性里的内容    method = re.compile(r'/(.*?)/')  # 定义正则方法,将不需要的地址筛选掉    flag = re.findall(method, child_path)  # 正则删选之后的结果    if len(flag) == 1:  # 规定长度为了防止角标越界的状况发生        if flag[0]:            params.append(child_path)

分析状况

目前而言,知道的有几个信息

  1. 昵称有五十多种类型
  2. 每一种昵称都有多个页面
  3. 每一页都有n条数据

从这里开始逐个击破,我们需要先遍历出每一种类型的每一页里的每一条数据

这里我们先从类型入手

拿到每种类型的页数

其实这里我完全可以少写个循环的...咳

# 昵称类型for i in range(len(params)):    index = 411  # 这个数字还是必要的 你可以看看 http://www.oicq88.com/shanggan/97.htm 和 http://www.oicq88.com/shanggan/411.htm 之间是否有任何区别    soup = BeautifulSoup(get_html(base_url + params[i] + '%s.htm' % index), 'lxml')  # 用一个巨大的数字,拿到一共有多少个页面    item = soup.find_all(name='a', attrs={'class': 'hover', 'target': '_self'})  # 这个就是用来拿到最后一页的内容的方法    index = int(item[0].text)  # 将最后一页的内容强转为int类型的数据,方便遍历每一页    # 后面这两个就是给个反馈,用户体验更加爽快    print('Current module is => ' + params[i] + 'And ...')    print('\nThe page count is => ' + str(index) + '\n')

遍历每一页,并将数据写入文件中

这里可以说下,soup.find之后的值被for in遍历出来的,就不是BeautifulSoup对象了,所以不能用同样的方式去查看子类内容了

不过子类内容可以直接再次被遍历(这个坑卡的时间有点久,而且写博客的时候我发现这个问题能有n种方式解决...)

# 分页!    for page in range(1, index):        print('\ncurrent page => ' + str(page) + '\n')        # 昵称项目 / 页        soup = BeautifulSoup(get_html(base_url + params[i] + str(page) + '.htm'), 'lxml')         try:            for ul in soup.find(name='ul', attrs={'class': 'list'}):                for ls in ul:                    for p in ls:                        # 写入文件                        try:                            with codecs.open(file_name, "a", 'utf-8') as f:  # 这个用的是 a 方法,意味着直接在文件后面补充,不删除之前内容(重写),如果用w的话,可能会让字符串超出范围,从而抛出异常                                f.write(p)                                f.write('\n')                                currentItem += 1                                print('The ' + str(currentItem) + ' => Done')                        except IOError:                            print(IOError)                        finally:                            f.close()        except ValueError:            print('The null item not can be iterable...')

到此,差不多就这样了,简单记录下,代码直接copy就可用,前提是环境装齐

对于python,还是要经常抛出下错误,免得各种问题阻断了进程...不然爬一半炸了,时间都白费了

  1. 最好让爬取的内容更可控
  2. 有一套日志系统记录抛出的错误(现在想想把except里的内容改成文件流操作就行了)
  3. 好像也没什么了

转载于:https://www.cnblogs.com/Arunoido/p/11140495.html

你可能感兴趣的文章
docker 镜像
查看>>
OAuth 2.0攻击面与案例总结
查看>>
centos7grub2 引导win10
查看>>
基于DCMTK的DICOM相关程序编写攻略
查看>>
win7下的IP-主机名映射
查看>>
Alpha版本项目展示
查看>>
朴素贝叶斯知识点概括
查看>>
CentOS7 通过代理上网
查看>>
Asp.net MVC中的ViewData与ViewBag
查看>>
HDU 1693 Eat the Trees
查看>>
Bootstrap 栅格系统 理解与总结
查看>>
oracle的for和i++
查看>>
YML(2)yml 语法
查看>>
线段树专辑——pku 2886 Who Gets the Most Candies?
查看>>
求一个字符串中连续出现的次数最多的子串
查看>>
寒假作业pta3
查看>>
ubuntu使用记录
查看>>
面试题:查询连续出现的数字
查看>>
JS简单实现自定义右键菜单
查看>>
一个妹子图应用客户端源码
查看>>