python爬取问题
Python编程学习圈 人气:3前言:
现在写爬虫,入门已经不是一件门槛很高的事情了,网上教程一大把,但很多爬虫新手在爬取数据的过程中依旧会遇到这样那样的问题。
今天整理了部分新手在爬虫过程中遇到的问题,希望可以给大家提供一点问题解决的思路和参考。
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有非常丰富的爬虫库可以直接使用,非常的方便。
加载全部内容