python爬虫抓取时常见的小问题总结
作者: Python编程学习圈
前言:
现在写爬虫,入门已经不是一件门槛很高的事情了,网上教程一大把,但很多爬虫新手在爬取数据的过程中依旧会遇到这样那样的问题。
今天整理了部分新手在爬虫过程中遇到的问题,希望可以给大家提供一点问题解决的思路和参考。
01 无法正常显示中文?
使用requests库或者urllib库获取源代码时无法正常显示中文;
r = requests.get('http://xxx')print r.text
使用requests解析中文网页时,上述语句在ipython中一直打印的都是乱码.......
试过如下代码:
import sys reload(sys) sys.setdefaultencoding('utf8')
还有类似:
r.text.decode('coding1').encoding('coding2')
都不能凑效!
解决方法
① requests库的文本中有两种类型
- 一种是文本类型,使用text属性,一种是针对音频、视频、图片等二进制数据类型,使用content属性。
- 一般返回的是text属性时会出现中文乱码现象,因此在输出返回之前需要显示的修改属性encoding,将其赋值为“utf-8”或者是apparent_encoding即可。
② urllib库的文本只有一种就是使用read()方法进行读取
因此要解决中文问题,一定要在读取后加入.decode(“utf-8”),进行显示的转码之后便不会出现乱码问题了。
02 加密问题
爬虫一般怎么解决加密问题?
① 对于网页端来说通常加密的算法是写在 js 代码里的,所以首先你要对 js 语言有所了解。
至少知道 js 基础的内容,其次找到对应 js 加密代码,然后找出关键的函数。
把 js 代码在 node.js 环境进行调试,最后在 Python 环境下利用 execjs 库去执行调试好的代码。
② 就是模拟浏览器环境直接获取渲染后的数据,最常用的手段就是利用 Selenium 框架了。
这种方式非常便利,当然对应的缺点就是效率非常低下。不过现在有新的框架来取代 Selenium,即 Puppeteer,这个框架你可以看出是异步版的 Selenium。
对于爬虫程序,一个万能公式:
爬虫程序 = 网络请求 + 数据解析 + 数据存储
这三部分就对应这爬虫的基础,任何一个爬虫程序都会保存这三部分的内容,一些复杂的爬虫无非是在此基础上添加些别的内容。一个爬虫工程师反爬能力有多强,他的爬虫实力就有多高。
03 获取不到网页的全部代码?
问题:通过request方法获取的网页代码与在浏览器看到的网页源码不一致;
解决方法:由于很多网页的数据传输是通过js命令传到网页的,因此使用request()方法不能获取通过js传递过来的信息代码。此时通过使用selenium库来模拟浏览器运行,就像真正的用户在操作一样,可通过此方法可获得该网页的源码。
from selenium.webdriver.support.wait import WebDriverWaitbrowser = webdriver.Chrome()browser.get(Url)html = browser.page_source
04 点击下一页时网页网页不变
问题:
在爬取的过程中,由于要爬取每个律师详细信息所对应的网址,因此涉及到翻页的问题,但在网页上实时点击下一页时发现网址并没有发生变化。
解决方法:
通过使用selenium中nextpagebutton.click()方法来模拟跳转到下一页,从而对下一页的内容进行获取。
nextpagebutton = browser.find_element_by_xpath('//*[@class="next_page"]') # 定位到“下一页”按钮 nextpagebutton.click() # 模拟点击下一页 wait = WebDriverWait(browser, 10) # 浏览器等待10s
05 文本节点问题
首先看两个HTML代码,这是你眼中的HTML代码:
这是计算机眼中的HTML代码:
解决方法:
在BS4中,我们在HTML中看到的换行符以及空格都是NavigableString 也就是文本节点。
06 如何快速找到提取数据?
解析网页时,如何快速找到数据存放的位置,并提取其中的数据?
这是很多新手会遇到的一个问题;就是虽然运行我的代码没有问题,大概逻辑也能读得懂,但是想修改一下爬取同类网站,或者同一个网站的其他数据时却不知从何下手。
这个其实是对工具使用不熟悉而已;我这里简单讲解一下(beautifulSoup)常用的使用技巧,当然它有很多强大便捷的功能。我这里只介绍几个常用的函数,这几个用好了一样可以应付几乎所有网站。首先,爬取之前需要定位到数据所在的标签,这个使用 F12 开发者工具中的这个按钮,点一下按钮,然后点一下网页,可以很快定位到页面中的相应标签。这步就不详细说了,很简单的,自己摸索一下。
接下来介绍如何用代码获取那个标签;
首先你观察你要找到的标签,是什么标签?是否有 class 或者 id 这样的属性(如果没有就找找它父标签有没有,尽量找这样的)因为 class 和 id 这两个属性作为筛选条件的话,查找到的干扰项极少,运气好的话,基本上可以一击必中。
这里介绍 beautifulSoup 中的两个函数,find 和 find_all 函数;
比如我们要获取上图中箭头所指的,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 网页了。
07 获取标签中的数据
查找到标签之后,又该如何获取标签中的数据呢?
标签中的数据位置,一般有两种情况:
<!--第一种,位于标签内容里--> <p>这是数据这是数据</p> <!--第二种,位于标签属性里--> <a href="/xxx.xxx_xx_xx.html" rel="external nofollow" ></a>
如果是第一种情况很简单,直接 pTip.text 即可;(pTip 是前面已经获取好的 p 标签)
如果是第二种情况,需要看它是在哪一个属性里的数据,比如我们要获取上面 a 标签中的 href 属性中的链接,可以 link = aTip["href"] 即可。(aTip 是前面已经获取好的 a 标签)。
08 去除指定内容
去除获取内容首尾的指定字符串;
问题:
有时我们在爬虫过程中,获取到的信息并不是全部都是我们想要获取的内容,如果想要去除指定内容。
例如:字符串 a = [“aaaa”],但是只想获取中间的aaaa
解决:
可以通过使用正则表达式来提取文本内容,去除符号,但如果使用此方法,字符串中间包含的符号也会被去掉。如果确定想要获取的文本中间不含有符号,可以选用正则表达式。
x = strTemp.xpath('./div[3]/div[1]/div/div/div[1]/div/ul/li[14]//text()') xx = str(x) email = re.search(r'[\u4e00-\u9fa5a-zA-Z0-9]+', xx)
第二种方法是使用strip()方法来去除首尾的符号;
a = td[1].get_text().strip('[""]')
09 转化为字符串类型
通过正则表达式获取的内容是match类型,如何转化为字符串str类型?
解决办法:使用group(0)来转化为字符串类型
email = email.group(0)
10 滥用遍历文档树
常见的方法有:
- contents
- descendants
- parent
- next_sibling
- next_element
这些方法都会遍历文档树中的所有节点,包括文本节点。也就是说只要你使用这些方法,你就一定会选择出许多文本节点,因为文本节点无处不在,换行、空格等。
解决方法:使用过滤器find等方法;
soup.find(name=‘tagname')
当我们一旦在过滤器中指定了name关键字,那么返回的结果就一定是tag对象,因为文档节点没有name属性。
结论:大多数情况下,你需要的是find 这一类过滤器,而不是遍历所有节点。
11 数据库保存问题
在将数据存储到数据库的过程中,遇到的一些问题如下:
1)爬取后的内容是列表形式,如何转化为字符串类型?
解决办法:使用str()方法
b = strTemp.xpath('./div[3]/div[1]/div/div/div[1]/div/ul/li[3]//text()') agee = str(b)
注意:数据库中对对应的字段类型要与python中一致;
2)python 与 mysql 数据库连接
connection = pymysql.connect(host='localhost', user='root', password='zaizaizai', db='layer', charset='utf8mb4') try: # 获取会话指针 with connection.cursor() as cursor: # 创建SQL语句 sql = "insert into layer(namee,organization,sex,age,nation,edu,leixing,zhengzhi,numberr,first_time,get_time,status,paizhu,sifaju,email) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) " # 执行SQL语句 cursor.execute(sql, (namee, organization, sex, age, nation, edu, leixing, zhengzhi, numberr, first_time, get_time, status, paizhu, sifaju, email)) # 提交 connection.commit() finally: connection.close()
3)保存在一个文件夹里的图片不停的被后来抓取的图片覆盖掉?可能是变量放在for 循环内外出现的问题,修改后解决此问题,用一些小例子做测试:
#测试一 x=0 for i in range(0,10): for j in range(0,10): print x x+=1 for i in range(0,10): x=0 for j in range(0,10): print x x+=1 #测试二 >>> x=0 >>> for i in range(0,10): print('the i is',i) x=0 for j in range(0,10): print('the j is',j) print('the x is',x) x+=1 >>> for i in range(0,10): print('the i is',i) x=0 for j in range(0,10): print('the j is',j) print('the x is',x) x=x+1
12 爬虫采集遇到的墙问题
主要表现就是访问不了了、访问几次就断了,然后手动访问,或者换个终端以后完全没问题。
面对这种情况,我们首先先想一下以下这几个问题:
- 爬虫是否过于频繁访问?影响了对方服务器的业务?
- 爬虫采集是否触犯了对方的防御机制?
- 爬虫是否有bug导致对方不堪重负?
以上这些主观问题是红线问题,是要考虑到的,自己要把握好爬虫的分寸,不能损坏他人合法利益。总结了一下遇到一些被管理员和防火墙拦截的问题和原因;
1)管理员不想让你访问,这种情况分两种:
第一种:管理员登录后台一看,有个家伙10个小时就访问了我的1万次,不带停的啊!如果是我,第一反应就是封了再说,肯定有问题,都不用分析。
第二种:我感觉有人在爬数据,我要把他找出来。
2)网站配置不让你访问这个主要是网站限制;比如tomcat或者Nginx,限制某个连接访问时间,因为如果这个连接一直在这里,然后不断有新的连接进来,那么连接池迟早满了,最终完蛋。
你这就是要DDOS攻击我,所以我可以设置多长时间你没动静,我就给你断了。
3)防火墙配置
这个就难了,因为防火墙管的很细,如果是服务器WAF那就很头疼,他在拦截DDOS的时候,就非常容易把爬虫也拦截掉。如果要绕过WAF,那就是一种入侵行为了,要从页面采集数据就只能欺骗WAF,继续刷新数据页面。
4)如何避开以上策略,主要就是欺骗和伪装。
逃避IP识别
通过采用或者构建IP代理,变换IP地址,减少单IP对规则的触发,封禁恶意IP是网站管理过程中最常用的方式。
变换请求内容
虽然你的IP变了,但还是这个浏览器、还是这个cookies记录,也会被识别。这个可以通过变换cookies和header进行伪装。
降低访问频率
一般如果单位时间内,访问网站的次数过高,很容易被判断为是CC攻击。并且会对服务器带来很大的压力,影响正常业务。这就背离了我们进行数据采集的初衷了,所以设置一个sleep(),降低一下刷新频率,减少一些对服务器资源的占用。
慢速攻击判别
慢了也会被干掉的!慢速攻击是 http 慢速攻击是利用http合法机制,在建立连接后尽量长时间保持连接,不释放,达到对HTTP服务攻击。攻击者发送POST请求,自行构造报文向服务器提交数据,将报文长度设置一个很大的值。
且在随后每次发送中,每次只发送一个很小的报文,这样导致服务器一直等待数据,连接始终一直被占用。如果攻击者使用多线程或傀儡机子去做同样操作,服务器WEB容器很快就被占满TCP连接而不再接受新请求,从而导致服务器崩溃、服务失效。这个最好就是采用多线程异步采集,同时及时把之前的连接关闭,并且控制数量进行。
总之,在采集数据的时候,多站在别人的角度上思考,采用别人能接受的方式获取数据,别人才能让你好好的获取数据。这是一个双赢的行为,不然就只能从入门到入牢的转变了,大家都不容易,互相给口饭吃。
13 验证码问题
验证码的问题一般如何解决?大体的思路有两种:
- 正向破解
- 逆向破解
正向破解
比如常见的图形验证码,你可以首先把图片保存下来,然后利用一些图文识别图去识别相应的内容。对于滑块验证码,你可以利用 Selenium 框架去计算缺口的距离,然后模拟鼠标拖动滑块。
逆向破解
这个就涉及到验证码的实现逻辑,你需要看懂对方验证码实现的逻辑。看看发送验证码请求的时候需要哪些参数,而这些参数又是怎么生成的,模拟请求。
逆向破解属于短暂型的省力做法,但相应的难度非常的大。-> 直接使用打码平台上面说两种方式都属于非常耗时耗力的行为,而且一旦对方网站反爬策略更新,你的代码就会失效。所以能花钱解决的事,大家就选择直接使用打码平台就好。
学会Python爬虫需要具备三部分的内容:
- Python 基础
- 爬虫基础
- 反爬的学习
这三部分内容是做爬虫的必备基础,主流语言是使用Python,Python有非常丰富的爬虫库可以直接使用,非常的方便。
到此这篇关于python爬虫抓取时常见的小问题总结的文章就介绍到这了,更多相关python问题总结内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!