最近有接手数据项目, 这两个项目有个共同点就是看上去都特别简单, 以为三两个小时就可以搞定, 但实际上里面的坑却特别多。 不完整做一遍, 只是在表面上理解完全不会体会到那种踩坑改bug的地狱般无助的感觉。
《黑客与画家》里说到, 一流的高手写代码都特别流畅, 一般思路对了, 不会关bug 什么事, 总是改bug 是非常令人羞耻的行为。不过还好本人对自己的编程能力要求不高, 也就做一下自己这方面的总结, 希望牛人不要光顾我的博客:)
我有一个习惯, 就是喜欢反复琢磨一个项目, 从里面挖掘出这个项目的通用解决方法和框架, 以后做的项目, 都在这个基础上进行改良和升级。所以这篇博客讲一下在写代码时遇到的卡点以及之后的改良。
题目大致方向: 采集中国理财网存续的理财产品信息。
其实是采集筛选类型下的其中一个叫存续的圆圈选项,出来一张表格, 一共有1546页。
卡点一: 发现此网站保密性工作做的极好,网站禁止右键。
解决方法: 查了无数禁止右键的博客, 找到了正确的解决方法:书签法 - 在chrome 中输入一个书签, 名称栏输入【解除右键限制】, 网址处输入:
javascript:(function() { function R(a){ona = "on"+a; if(window.addEventListener) window.addEventListener(a, function (e) { for(var n=e.originalTarget; n; n=n.parentNode) n[ona]=null; }, true); window[ona]=null; document[ona]=null; if(document.body) document.body[ona]=null; } R("contextmenu"); R("click"); R("mousedown"); R("mouseup"); R("selectstart");})()
保持为书签, 回到网站,点击书签“【解除右键限制】”,网页上即可右键。
卡点二: 在headers里, 看到request method 是post 不是get
解决方法:当写完了代码,经过多方排除,发现获取到的数据什么也没有,就查看request 的语句到底有什么问题。知道post 的方法后,知道preview 里不再是prementer,而是form data. 修改为:
res = requests.post(url, headers=headers, data = json.dumps(FormData))
卡点三: 出现一个报错。AttributeError: ‘set’ object has no attribute 'items’.
解决方法: headers= {‘User-Agent’ : ‘value’ } 而不是 headers= {‘User-Agent : value’ }url='https://www.chinawealth.com.cn/LcSolrSearch.go' url 必须加引号。
卡点四: 爬虫时用requests解析到的内容与原网页内容不一致。
解决方法: 在尝试在headers 里加入cookie, host , referer 之后仍然没有解决问题。
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Cookie':'BIGipServerPool_SuperFusion_LiCai_Nginx_8080=!U0gH0cZ/zj/u6zTw/RB9YiZAVIlwUwMdvUFcBfYQk4V8SlUpU1mLYC2XWsn3q6BpQUlLHx7DEIykIrY=; BIGipServerPool_SuperFusion_LiCai_fe_8080=2802057494.36895.0000; JSESSIONID=68036A67BB270B345BF228AAFC99FBBF; _pk_ses.3.8bc7=*; _pk_id.3.8bc7=e467ba032a815a22.1660469265.1.1660469266.1660469265.; _pk_ses.12.8bc7=*; _pk_id.12.8bc7=6cd8e9027d705fba.1660469267.1.1660469441.1660469267.',
'Host': 'www.chinawealth.com.cn',
'referer':'https://www.chinawealth.com.cn/zzlc/jsp/lccp.jsp' }
之后想到用selenium 模块。 先附常规解题法
import requests
import csv
import json
csv_file = open('存储产品.csv','w', newline = '', encoding = 'utf-8')
writer = csv.writer(csv_file)
list2 = ['产品名称','登记编码','期限类型','业绩比较基准下限(%)']
writer.writerow(list2)
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Cookie':'BIGipServerPool_SuperFusion_LiCai_Nginx_8080=!U0gH0cZ/zj/u6zTw/RB9YiZAVIlwUwMdvUFcBfYQk4V8SlUpU1mLYC2XWsn3q6BpQUlLHx7DEIykIrY=; BIGipServerPool_SuperFusion_LiCai_fe_8080=2802057494.36895.0000; JSESSIONID=68036A67BB270B345BF228AAFC99FBBF; _pk_ses.3.8bc7=*; _pk_id.3.8bc7=e467ba032a815a22.1660469265.1.1660469266.1660469265.; _pk_ses.12.8bc7=*; _pk_id.12.8bc7=6cd8e9027d705fba.1660469267.1.1660469441.1660469267.',
'Host': 'www.chinawealth.com.cn',
'referer':'https://www.chinawealth.com.cn/zzlc/jsp/lccp.jsp' }
url='https://www.chinawealth.com.cn/LcSolrSearch.go'
for i in range(1546):
FormData = {
'cpjglb':'None',
'cpyzms':'None',
'cptzxz':'None',
'cpfxdj':'None',
'cpqx': 'None',
'mjbz': 'None',
'cpzt': '04',
'mjfsdm':' 01','NA'
'cpdjbm':'None',
'cpmc': 'None',
'cpfxjg':'None',
'yjbjjzStart':'None',
'yjbjjzEnd': 'None',
'areacode':'None',
'pagenum':'str(i+1)',
'orderby':'none',
'code': 'none'
}
res = requests.post(url, headers=headers, data = json.dumps(FormData))
print(res.status_code)
articles =res.json()
print(articles)
lists = articles['List']
for i in lists:
list1 = [i['cpms'],i['cpdjbm'],i['qxms'],i['yjbjjzxx']]
writer.writerow(list1)
csv_file.close()
print('okay')
卡点五: selenium 安装不上。输入pip install selenium 一直断联
解决方法: 进入 pypi.org/, 在下载页面下载whl 文件, 在终端输入
pip install /Users/apple/Desktop/selenium-4.4.3-py3-none-any.whl
Processing ./Desktop/selenium-4.4.3-py3-none-any.whl
也就是把桌面上的whl 文件拖入install 后面, 安装成功。
给出初步selenium 代码,爬取网页表格数据。
卡点六:出现bug, FileNotFoundError: [Errno 2] No such file or directory: 'chromedriver': 'chromedriver'
解决方法: 提示没有chromedriver.exe(谷歌浏览器驱动)这个文件, chrome driver没有放置在正确的路径下。 下载chromedriver http://chromedriver.storage.googleapis.com/index.html
打开终端 输入
open /usr/local/bin/
将下载到的chromedriver拷贝进去, 给chromedriver 运行权限, 输入apple password
sudo chmod u+x,o+x /usr/local/bin/chromedriver
看安装的版本号
chromedriver --version
出现bug , 打不开 chromedriver?
输入代码
cd /usr/local/bin/
打开隐私模式, 增加信任
xattr -d com.apple.quarantine chromedriver
再次输入看完装的版本号, 就可以了。
把chromedriver 的完整路径用终端提取出来, 把文件拖入终端
/usr/local/bin/chromedriver
最后在webdriver中加入chromedriver的路径,彻底解决找不到chromedriver的路径的问题。
附上五种方法,教你如何在Mac上查看文件完整路径 - 百度文库
browser = webdriver.Chrome('/usr/local/bin/chromedriver')
卡点七:解决DeprecationWarning: Executable executable_path has been deprecated, please pass in a Service object in Selenium Python
解决方式: 该类型的警告大多属于版本更新时,所使用的方法过时的原因;某方法在当前版本被重构,依旧可以传入参数,但是在之后的某个版本会被删除。
写入代码, 问题解决
import csv
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
chrome_options = Options()
chrome_options.add_argument('--headless')
s = Service(executable_path=r'/usr/local/bin/chromedriver')
driver = webdriver.Chrome(service = s ,options = chrome_options)
初步selenium 代码
import csv
from selenium import webdriver
import time
from selenium.webdriver.chrome.service import Service
s = Service(executable_path=r'/usr/local/bin/chromedriver')
driver = webdriver.Chrome(service = s)
csv_file = open('存续产品.csv','w', newline = '', encoding = 'utf-8')
writer = csv.writer(csv_file)
driver.get('https://www.chinawealth.com.cn/zzlc/jsp/lccp.jsp')
def GetTableElements():
driver = GetDriver()
elements = driver.find_elements_by_tag_name('table')
return elements
def _GetHeadList(table):
trs = elements.find_elements_by_tag_name('tr').find_elements_by_class_name('pro_title')
res1 =[]
for tr in trs:
res1.append(tr.text)
return res1
def _GetOneRow(tr, heads):
res2={}
tds = tr.find_elements_by_tag_name('td')
for i, td in enumerate(tds):
res2[heads[i]]= td
return res2
def GetTable(table):
res3 =[]
heads = _GetHeadList(table)
tbody = table.find_element_by_tag_name('tbody')
trs = tbody.find_elements_by_tag_name('tr')
for tr in trs:
row = _GetOneRow(tr, heads)
res3.append(row)
return res3
writer.writerow(res3)
csv_file.close()
driver.close()
卡点八: 输出的csv文档空白,
解决方式: 通过排查,发现是函数的代码无法输出结果,换一种代码方式。我们看到table 类型的网页结构大概是这样。
<table class="..." id="...">
<thead>
<tr>
<th>...</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
...
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
</tbody>
</table>
通过pandas 来做。
import csv
from selenium import webdriver
import pandas as pd
from selenium.webdriver.chrome.service import Service
s = Service(executable_path=r'/usr/local/bin/chromedriver')
driver = webdriver.Chrome(service = s)
csv_file = open('存续产品.csv','w', newline = '', encoding = 'utf-8')
writer = csv.writer(csv_file)
driver.get('https://www.chinawealth.com.cn/zzlc/jsp/lccp.jsp')
datalist=[]
header=[i for i in driver.find_elements_By.cssSelector("table>tr>pro_title")if i.text],
rows=[i for i in driver.find_elements_By.cssSelector("tbody>tr") if i.text]
for row in rows:
datalist.append([i for i in row.find_elements_By.cssSelector("td") if i.text])
df=pd.DataFrame(datalist,index=header)
writer.writerow(df)
csv_file.close()
driver.close()
卡点九: WebDriver‘ object has no attribute ‘find_element_by'
解决方法: find_element_by 语法错误, 更换语法。
driver.get('https://www.chinawealth.com.cn/zzlc/jsp/lccp.jsp')
datalist=[]
header=[i for i in driver.find_element(By.CSS_SELECTOR,"#table>tr>pro_title")if i.text],
rows=[i for i in driver.find_elements(By.CSS_SELECTOR,"#tbody>tr") if i.text]
for row in rows:
datalist.append([i for i in row.find_elements(By.CSS_SELECTOR,"td") if i.text])
df=pd.DataFrame(datalist,index=header)
writer.writerow(df)
卡点十: no such element: Unable to locate element: {"method":"css selector","selector":"#table>tr>pro_title"}
解决方案: 重新复习标签,属性和元素, 复习css_selector 的用法。修改header 等代码。
datalist=[]
header =[i for i in driver.find_element(By.CSS_SELECTOR,"#tbody>tr['class = pro_title']")if i.text],
rows=[i for i in driver.find_elements(By.CSS_SELECTOR,"#tbody>tr") if i.text]
for row in rows:
datalist.append([i for i in row.find_elements(By.CSS_SELECTOR,"td") if i.text])
df=pd.DataFrame(datalist,index=header)
writer.writerow(df)
卡点十一: selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: An invalid or illegal selector was specified.
解决方案; 表达错误, 重新表达
datalist=[]
header =[i for i in driver.find_element(By.CSS_SELECTOR,'tbody>tr[class = 'pro_title']')if i.text],
rows=[i for i in driver.find_elements(By.CSS_SELECTOR,'tbody>tr') if i.text]
for row in rows:
datalist.append([i for i in row.find_elements(By.CSS_SELECTOR,'td') if i.text])
df=pd.DataFrame(datalist,index=header)
writer.writerow(df)
csv_file.close()
卡点十二: selenium.common.exceptions.InvalidArgumentException: Message: invalid argument: 'value' must be a string.
解决方案:注意class 属性的定位方法, 参照 小红书,CssSelector定位详解 - 百度文库,Selenium之find_element_by_css_selector()的使用方法 - 灰信网(软件开发博客聚合)发现多标签层次和class=pro_title 的用法 以及value is string的解决方法为 'tbody>tr.pro_title'
import csv
from selenium import webdriver
import pandas as pd
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
s = Service(executable_path=r'/usr/local/bin/chromedriver')
driver = webdriver.Chrome(service = s)
csv_file = open('存续产品.csv','w', newline = '', encoding = 'utf-8')
writer = csv.writer(csv_file)
driver.get('https://www.chinawealth.com.cn/zzlc/jsp/lccp.jsp')
datalist=[]
header =[i for i in driver.find_element(By.CSS_SELECTOR,'tbody>tr.pro_title')if i.text]
rows=[i for i in driver.find_elements(By.CSS_SELECTOR,'tbody>tr') if i.text]
for row in rows:
datalist.append([i for i in row.find_elements(By.CSS_SELECTOR,'td') if i.text])
df=pd.DataFrame(datalist,index=header)
writer.writerow(df)
csv_file.close()
driver.close()
网站显示故障。 但是代码目测大概率正确。
任务基本完成。 最后总结一句: 只有做对了, 才能享受其中!