Python 网络爬虫:爬虫的一些小技巧以及常见问题合集

从开始玩爬虫到现在差不多半年多了,写了不少爬虫,爬了不少网站,在博客里也分享了不少爬虫的教程。

我的教程文章中,一般会附带完整的爬虫代码,大家只要搭建好环境,便可以直接运行使用。不少读者朋友在使用爬虫遇到问题时也会跟我讨论,交流过程中我发现了一些比较共性的问题。

  1. 因此文章重点放在了思路分析上,具体如何编写代码爬取数据则简单略过。造成了一些读者,基于我的代码进行修改爬取其他相似网站时束手无策。

  2. 由于写代码时有些疏忽,以及没有考虑到各人系统环境默认编码等设置的差异,导致读者们运行代码时出现了一些常见的 Bug,比如爬取到的数据乱码问题,保存文件时路径错误等问题。

针对这些问题,我打算将我玩爬虫时的一些经验心得,供大家参考,如果有哪里说的不对,或者有更好的方法的话,欢迎大佬们批评指正,交流补充。


1. 设置请求头 Headers 的问题

  • 可以通过设置 headers 将爬虫伪装成浏览器,现在大部分的网站不这样伪装一下根本爬不了。
    headers = {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }
  • 一般 headers 设置 user-Agent 即可,如果有的数据是登陆后才能看到的话,还需要添加 cookies 参数(先登陆账号后,在浏览器的开发者工具中,拷贝 Cookies 即可)。这些参数都可以在浏览器的开发者工具中找到。
image
  • 注意,如果要分享代码给别人的话,代码中的 cookie 数据一定要删掉,否则别人可以通过这个直接免密登陆你的账户。

2. 为什么我爬到的数据是乱码?

一般大家看到乱码,下意识的觉得是代码有问题爬错东西了。其实不然,这个就是编码的问题,解决方式也很简单。
程序中涉及到编码格式的地方有两处,一处是在发起请求后,对返回的内容进行解码;另一处是在保存文件时,设置编码格式。下面我们分开来说。

  1. 发起请求,获取网页内容阶段。一般的网站的编码格式都是 UTF-8,所以当你系统的默认编码也是 UTF-8 时,也就是说,你的默认编码方式和目标网站的编码方式一致时,即使不明确设置编码方式,也不会出问题。但是如果不一致,便会出现乱码。这也是为什么经常有 “明明在我电脑上运行是好的,为什么在你电脑上就乱码了” 这样的问题。
    这种问题解决也很简单,只要在代码中设置一下 encoding 即可。
    这里建议一种方法,r.encoding = r.apparent_encoding ,这个可以自动推测目标网站的编码格式,省的你自己去一个个设置(当然极少数情况下它可能会推测错误出现乱码,到时候你再手动去查看网页编码,手动设置吧)。
def fetchURL(url):
    headers = {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }

    r = requests.get(url,headers=headers)
    r.raise_for_status()
    # 这里设置编码格式
    r.encoding = r.apparent_encoding
    return r.text

  1. 保存文件时的编码错误。
    这个是读者朋友们反映较多的一个问题,就是爬取过程中没问题,但是用 excel 打开保存好的 csv 文件时出现乱码(用记事本打开没问题)。
    这个其实就是文件的编码方式和 Excel 的解码方式不一致导致的。在 dataframe.to_csv 这句,参数里添加一个 encoding='utf_8_sig',指定文件的编码格式,应该就可以解决了。
def writePage(urating):
    '''
        Function : To write the content of html into a local file
    '''
    import pandas as pd
    dataframe = pd.DataFrame(urating)
    dataframe.to_csv('filename.csv',encoding='utf_8_sig', mode='a', index=False, sep=',', header=False )

之前乱码的 csv 文件,可以用记事本打开,然后点另存为,然后选择编码格式,ANSI ,unicode,UTF-8 都可以,然后保存之后,再次用 excel 打开就是正常的了。

如果你不知道你的乱码是哪种问题,有一个简单的判断方法,就是用记事本打开 csv 文件,如果正常显示,那么无误就是第二种情况,如果是乱码,那么很有可能是第一种情况。

3. 解析网页时,我如何快速找到数据存放的位置,并提取其中的数据?

这个也是很多读者朋友遇到的问题,就是虽然运行我的代码没有问题,大概逻辑也能读得懂,但是想修改一下爬取同类网站,或者同一个网站的其他数据时,却束手无策,不知从何下手。

这个其实是对工具使用不熟悉而已,我这里简单讲解一下(beautifulSoup)常用的使用技巧(当然它有很多强大便捷的功能,我这里只介绍几个常用的,很好用的函数,这几个用好了一样可以应付几乎所有网站)

首先,爬取之前需要定位到数据所在的标签,这个使用 F12 开发者工具中的这个按钮,点一下按钮,然后点一下网页,可以很快定位到页面中的相应标签,具体就不详细说了,自己摸索一下,很简单,很好用的。

开发者工具

接下来正式介绍,如何用代码获取到前面找到的那个标签。

这里介绍 beautifulSoup 中的两个函数,find 和 find_all 函数。

首先你观察你要找到的标签,是什么标签,是否有 class 或者 id 这样的属性(如果没有就找找它父标签有没有,尽量找这样的),因为 class 和 id 这两个属性作为筛选条件的话,查找到的干扰项极少,运气好的话,基本上可以一击必中。

image.png

比如我们要获取上图中箭头所指的,id 为 ozoom 的 div 标签时,我们可以这样

# html 是之前发起请求获取到的网页内容
bsobj = bs4.BeautifulSoup(html,'html.parser')
# 获取 id 为 ozoom 的 div 标签
# 根据 id 查找标签
div = bsobj.find('div', attrs = {'id' : 'ozoom'})

# 继续获取 div 下的 class 为 list_t 的 div 标签
# 根据 class 查找标签
title = div.find('div', attrs = {'class': 'list_t'})

注:如果标签有 id 属性的话尽量用 id 来查找,因为整个页面 id 是唯一的。用 class 查找的话,最好现在浏览器的网页源码中 Ctrl + F 搜索一下,相同 class 的标签有多少(如果比较多的话,可以尝试先查找他的父标签,缩小范围之后再查找)。

然后我们再讲讲 find_all 函数,适用于一次性查找一类型的很多标签的情况,比如下图这种情况

列表中的每一个 li 标签中,都是一条数据,我们需要将它们都获取到,如果是用前面的 find 函数的话,每次只能获取一个 li 标签。所以我们需要使用 find_all 函数,一次性获取所有符合条件的标签,存储为数组返回。

首先,由于 li 标签没有 id 也没有 class ,而页面中存在很多无关的干扰的 li 标签,所以我们需要先从它的父标签往上找,缩小查找范围,找到 id 为 titleList 的 div 标签之后,观察一下,里面的 li 标签都是需要的,直接 find_all 函数一下都获取完。

# html 是获取的目标网页内容
html = fetchUrl(pageUrl)
bsobj = bs4.BeautifulSoup(html,'html.parser')

pDiv = bsobj.find('div', attrs = {'id': 'titleList'})
titleList = pDiv.find_all('li')

基本上,把 find 和 find_all 函数组合使用,用熟练了可以应付几乎所有的 html 网页了,真的是,一招鲜吃遍天。

4. 查找到标签之后,我该如何获取标签中的数据呢?

标签中的数据位置,一般有两种情况。

<!--第一种,位于标签内容里-->
    <p>这是数据这是数据</p>

<!--第二种,位于标签属性里-->
    <a href="/xxx.xxx_xx_xx.html"></a>

如果是第一种情况,很简单,直接 pTip.text 即可(pTip 是前面已经获取好的 p 标签)。

如果是第二种情况,需要看它是在哪一个属性里的数据,比如我们要获取上面 a 标签中的 href 属性中的链接,可以 link = aTip["href"] 即可。(aTip 是前面已经获取好的 a 标签)。

5. 有些数据不能直接用,我需要从中提取想要的关键数据,但是我正则表达式不太会怎么办?

使用正则表达式处理字符串,用好了确实很厉害,但是门槛也相对较高。不仅学起来麻烦还容易忘,于是我就发现,其实一些简单的功能,使用字符串自带的 拼接 ,分割(split)等功能就可以很好地实现。

比如我获取到一个网址链接 html/2019-05/06 ,我需要从中提取出包含的年、月、日信息。我可以这样。

url = 'html/2019-05/06'
# 先根据 / 分割,得到 group1
group1 = url.split('/')
# 分割完后,group1 = ["html","2019-05","06"]
day = group1[2]        # day = "06"

# 将 group1[1],也就是 2019-05 进行分割,根据 - 分割成 group2
group2 = group1[1].split("-")
# 分割完成后,group2  = ["2019","05"]
year = group2[0]       # year = "2019"
month = group2[1]      # month = "05"

除了正则表达式之外,我比较喜欢用这个,小小的一个函数,用的熟练之后,还是能做不少事情的。

6. 为什么保存文件时候会报错 “FileNotFoundError:No such file or directory” 呢?

Python 在保存文件时,如果路径下你要操作的文件不存在,它会自动创建一个文件,然后写入数据。

但是,如果是路径中的文件夹不存在,则不会自动创建,而是会报错上面那样的错误。

所以啊,以后看到这样的错误,不要慌,只是你的路径中没有对应的文件夹而已,缺哪个文件夹,自己手动创建好,再运行就好啦。

如果你跟我一样比较懒,想让代码自动创建文件夹,也很简单,只需要在保存文件前,执行这些代码即可。

import os
# 如果没有该文件夹,则自动生成
if not os.path.exists(path):
    os.makedirs(path)

7. 最后再说一说爬虫的一些原则

  1. 指定爬取策略时,尽可能少的发起网络请求。一次请求能解决的事儿,尽量不要多次。
  2. 控制发起请求的频率,过于频繁的访问,不仅给对方服务器带来压力,也容易被对方反爬虫系统针对。
  3. 写爬虫要心怀敬畏之心,不当使用爬虫容易触犯法律。
  4. 爬虫毕竟只是工具,是手段,真正重要东西的数据,所以,不要为了使用爬虫而使用爬虫,数据量较小,能手动解决的尽量手动解决。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,421评论 1 45
  • 第一部分 创建爬虫 重点介绍网络数据采集的基本原理 : 如何用 Python 从网络服务器 请求信息,如何对服务器...
    万事皆成阅读 2,037评论 0 5
  • 网络参数 1.header下(1)user-agent:这个是爬虫header里可以说必须的东西,基本网站肯定第一...
    dawsonenjoy阅读 937评论 0 4
  • 1. 概述 本文主要介绍网络爬虫,采用的实现语言为Python,目的在于阐述网络爬虫的原理和实现,并且对目前常见的...
    Lemon_Home阅读 2,727评论 0 21
  • 上网原理 1、爬虫概念 爬虫是什麽? 蜘蛛,蛆,代码中,就是写了一段代码,代码的功能从互联网中提取数据 互联网: ...
    riverstation阅读 8,030评论 1 2