上一篇文档简单介绍了使用Python爬取招标网站的基本概念。在这个文档中,将说明爬虫的具体实现方法。
一、 整体业务流程
1)将目标网站录入到外部存储的Excel文档中,需要记录:招标网站的名称和网址
# read tendering website list
# csvPath='tendering2.csv'
# df = pd.read_csv(csvPath, engine='python')
# tenderDb = df.values
tenderDb=np.array([
['北京地铁电子商务采购平台', r'http://www.bjmetro.com.cn/ecp/rfq'],
['中煤招标有限责任公司', r'http://www.zmzb.com/gghw/index.jhtml']
])
# tenderDb = pd.DataFrame({'company': data[:, 0], 'website': data[:, 1]})
lenTDB = len(tenderDb)
这里为了便于调试,用一个数组替换了外部文件tendering2.csv
2)初始化配置文件和输出目录
# initialize configuration files directory
jasonPath = './jasonFiles'
if not os.path.exists(jasonPath):
os.makedirs(jasonPath)
# initialize data storage files directory
dbPath = './db'
if not os.path.exists(dbPath):
os.makedirs(dbPath)
如果用于配置的json文件夹不存在,那么创建这个文件夹。同样处理中间输出的结果
3)遍历招标网站
for i in range(lenTDB):
url = tenderDb[i,1].strip()
jsonFile = re.sub(r'(.*://)?([^/?]+).*', '\g<2>', url)
dbFile = os.path.join(dbPath, jsonFile + '.txt')
jsonFile = os.path.join(jasonPath, jsonFile + '.json')
在这个过程中,首先将网站域名作为唯一值,并作为配置文件和输出文件的文件名,例如:北京地铁的配置文件应该是www.bjmetro.com.cn.json,输出文件为www.bjmetro.com.cn.txt
4)指定的网站读取特定的页面
if os.path.exists(jsonFile):
f = open(jsonFile,"rb")
Cfg = json.load(f)
f.close()
f2 = open(dbFile,'w+',encoding='utf8')
for page in Cfg['pages']:
url = page['page']
regEx = page['regex']
group = page['group']
strFetched = getPageContents(url)
strFetched = re.sub(r"^\s+", "", strFetched, flags=re.M)
#f2.write(strFetched)
if regEx=='':
continue
matches = re.finditer(regEx, strFetched, re.MULTILINE | re.DOTALL)
print(tenderDb[i,0],url)
print(regEx)
print(len(strFetched))
matchNum = 0
for matchNum, match in enumerate(matches, start=1):
strScraped = ''
for i in group:
strScraped = strScraped + '\t' + match.group(i)
f2.write(strScraped.strip()+"\n\r")
print(matchNum)
f2.close()
else:
x = {
"pages": [
{
"page": "",
"regex": "",
"group": [1,2,3]
}
]
}
with io.open(jsonFile, 'w') as db_file:
db_file.write(json.dumps(x, indent=1))
在这一段代码中,如果配置文件存在,那么就根据配置文件读取页面。配置文件www.zmzb.com.json示例如下:
{
"pages": [
{
"page": "http://www.zmzb.com/gghw/index.jhtml",
"regex": "<li>\\s*<a\\shref=\\\"([^\\\"]*)\\\"\\starget=\\\"_blank\\\"\\stitle=\\\"([^\\\"]*)\\\">\\s*<span class=\\\"bidLink\\\">[^<]*<\/span>\\s*<span\\sclass=\\\"bidDate\\\" title=\\\"[^\\\"]*\\\">\\s*(\\d\\d\\d\\d-\\d\\d-\\d\\d)\\s*<\/span>\\s*<\/a>\\s*<\/li>",
"group": [3, 2, 1]
}
]
}
这是一个json,标准的数据交换格式。
为什么要在pages下面设置多个page呢?请各位思考
我们将一个页面(page)中的html读取后,保存在strFetched变量中,并将无用的空格去除。然后通过正则表达式regex,匹配出需要的内容,正则表达式使用括号对匹配结果进行分组,那么在json的group中定义了输出的组和顺序,在示例中为[3,2,1]表示,输出第3组匹配、第二组和第一组,并且顺序为3-2-1
匹配后得到的数据如下:
2020-02-14 中煤晋中能源化工有限责任公司下属九鑫公司2020年度洗油采购二次公告 http://www.zmzb.com:80/gghw/142625.jhtml
2020-02-14 中煤晋中能源化工有限责任公司下属九鑫公司2020年度纯碱采购二次公告 http://www.zmzb.com:80/gghw/142624.jhtml
2020-02-14 内蒙古中煤远兴能源化工有限公司变换催化剂、固体吸附剂采购项目【重新招标】终止公告 http://www.zmzb.com:80/gghw/142526.jhtml
2020-02-12 煤科集团沈阳研究院有限公司煤矿用激光甲烷遥测仪采购项目二次公告 http://www.zmzb.com:80/gghw/142503.jhtml
2020-02-12 平朔集团公司2020年1月第1批(包1-3)年度物资采购包2灯架等17项二次公告 http://www.zmzb.com:80/gghw/142489.jhtml
2020-02-12 平朔集团公司2020年2月第1批(包1-3)年度物资采购招标招标公告 http://www.zmzb.com:80/gghw/142482.jhtml
2020-02-11 平朔集团公司2020年1月第1批洗选中心空压机设备采购(CS19-4-1-10重新招标)招标公告 http://www.zmzb.com:80/gghw/142427.jhtml
2020-02-11 平朔集团公司2020年2月第1批(包2-5)长协寄售物资采购招标公告 http://www.zmzb.com:80/gghw/142426.jhtml
2020-02-11 中天合创能源有限责任公司 葫芦素煤矿矸石脱介振动筛采购二次公告 http://www.zmzb.com:80/gghw/142407.jhtml
2020-02-11 中煤科工集团上海有限公司采煤机安全准入分析验证实验室材料试验机采购二次公告 http://www.zmzb.com:80/gghw/142385.jhtml
2020-02-11 中煤科工集团上海有限公司采煤机安全准入分析验证实验室实时喷雾粒度分析仪采购二次公告 http://www.zmzb.com:80/gghw/142384.jhtml
2020-02-10 平朔集团公司2020年2月第1批(包4-6)年度物资采购招标公告 http://www.zmzb.com:80/gghw/142341.jhtml
2020-02-10 内蒙古中煤蒙大新能源化工有限公司埃利奥特配件(2001)采购二次公告 http://www.zmzb.com:80/gghw/142313.jhtml
2020-02-10 中煤鄂尔多斯能源化工有限公司阀门采购二次公告 http://www.zmzb.com:80/gghw/142292.jhtml
2020-02-10 中煤鄂尔多斯能源化工有限公司仪表材料采购二次公告 http://www.zmzb.com:80/gghw/142291.jhtml
2020-02-06 平朔集团公司2019年12月第2批能源化工(包1)物资采购 包1 二异丙基醚1项二次公告 http://www.zmzb.com:80/gghw/142153.jhtml
5)如果没有创建正则表达式
else:
x = {
"pages": [
{
"page": "",
"regex": "",
"group": [1,2,3]
}
]
}
with io.open(jsonFile, 'w') as db_file:
db_file.write(json.dumps(x, indent=1))
如果是第一次录入某个招标网站,会为该网站创建一个空的json
如果再次执行时,正则表达式regex没有定义,那么将跳过该记录
if regEx=='':
continue
二、为什么要使用Selenium
为什么不能使用request直接抓取html呢?大家可以从本示例中北京地铁网站和中煤招标网站的html区别来找到答案,请各位思考...
def getPageContents(url):
#options = Options()
#options.add_argument("--headless")
#driver = webdriver.Firefox(options=options, executable_path="C:\\Program Files\\Mozilla Firefox\\geckodriver.exe")
options = webdriver.ChromeOptions()
options.add_argument("headless")
driver = webdriver.Chrome(options=options)
driver.get(url)
soup = BeautifulSoup(driver.page_source, 'html.parser')
driver.quit()
return(soup.prettify())
三、如何创建正则表达式
1)正则表达式的基本语法
2)正则表达式的检测
3)在json中的正则表达式需要注意escape
四、还没有做的工作
1)爬取了数据后,被存储为txt或Excel文件。需要写个简单的逻辑,查询将要写入的新数据是否已经在文件中存在,即:是否是真正的新数据?
这个比较简单,留给大家完成。
2)发现新数据如何做出标记?并通过诸如:邮件、短信、微信发给相关的人
邮件可能比较简单
3)如何做到自动定期执行?
在linux上有cron定时命令,显然这是一个服务器环境,这个需要从整体规划考虑
4)如何实现过滤
这个比较简单实现,未来可以考虑订阅列表,不同的用户选择一组关键词,那么通过匹配关键词,向订阅用户发送新的招标信息。
5)按照规则补充其它网站、不同页面的json配置文件,编写正则表达式
有点工作量
网友评论