亲宝软件园·资讯

展开

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库的文本中有两种类型

② 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 滥用遍历文档树

常见的方法有:

这些方法都会遍历文档树中的所有节点,包括文本节点。也就是说只要你使用这些方法,你就一定会选择出许多文本节点,因为文本节点无处不在,换行、空格等。

解决方法:使用过滤器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 爬虫采集遇到的墙问题

主要表现就是访问不了了、访问几次就断了,然后手动访问,或者换个终端以后完全没问题。​

面对这种情况,我们首先先想一下以下这几个问题:

以上这些主观问题是红线问题,是要考虑到的,自己要把握好爬虫的分寸,不能损坏他人合法利益。总结了一下遇到一些被管理员和防火墙拦截的问题和原因;

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有非常丰富的爬虫库可以直接使用,非常的方便。

加载全部内容

相关教程
猜你喜欢
用户评论