易车网爬虫 项目总结

技术栈与环境

  • 语言与版本:Python 2.7
  • 无界面浏览器:PhantomJS(配合 Selenium 驱动)
  • 自动化库:selenium
  • 数据库:Oracle(cx_Oracle 驱动)
  • 其他:re(正则)、time、logging

依赖安装(Python 2.7):

1
2
pip install "selenium<4"  # 3.x 适配 PhantomJS
pip install cx_Oracle

PhantomJS 下载与配置:

核心流程

  1. 启动 Selenium(PhantomJS)并访问易车网入口页(或登录页)。
  2. 执行登录(如需),等待页面加载完成。
  3. 进入列表页,循环分页,逐条打开详情页或在列表直接解析。
  4. 用正则或 DOM 解析提取字段,组织为结构化数据。
  5. 连接 Oracle,执行参数化插入,提交事务。
  6. 去重(触发器或唯一约束),记录日志,异常重试与限速。

启动 PhantomJS 与登录示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.PhantomJS(
executable_path=r'D:/tools/phantomjs-2.1.1-windows/bin/phantomjs.exe'
)
driver.set_page_load_timeout(60)

def login_bitauto():
driver.get('https://www.bitauto.com/')
# 示例:等待某个元素出现(按实际页面改选择器)
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, 'input[name="kw"]'))
)
# 如需账号登录:
# driver.find_element_by_css_selector('#username').send_keys('your_user')
# driver.find_element_by_css_selector('#password').send_keys('your_pass')
# driver.find_element_by_css_selector('button.submit').click()

login_bitauto()

列表与详情解析示例

方式一(DOM 解析):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def parse_list_page():
items = []
for li in driver.find_elements_by_css_selector('ul.list > li.item'):
title = li.find_element_by_css_selector('h3 a').text
price = li.find_element_by_css_selector('.price').text
link = li.find_element_by_css_selector('h3 a').get_attribute('href')
items.append({'title': title, 'price': price, 'link': link})
return items

def parse_detail_page(url):
driver.get(url)
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, 'h1.title'))
)
data = {
'car_name': driver.find_element_by_css_selector('h1.title').text,
'price': driver.find_element_by_css_selector('.price .num').text,
'year': driver.find_element_by_css_selector('.info .year').text,
'mileage': driver.find_element_by_css_selector('.info .mile').text,
'city': driver.find_element_by_css_selector('.info .city').text,
'link': url,
}
return data

方式二(正则,对 page_source 解析):

1
2
3
4
5
6
7
8
import re

def parse_detail_by_regex(html):
m1 = re.search(r'<h1[^>]*>(.*?)</h1>', html)
m2 = re.search(r'class="price"[^>]*>.*?(\d+[.,]?\d*)', html, re.S)
car_name = m1.group(1).strip() if m1 else ''
price = m2.group(1) if m2 else ''
return {'car_name': car_name, 'price': price}

分页抓取与限速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time

def crawl_pages(max_pages=5):
results = []
for page in range(1, max_pages + 1):
url = 'https://www.bitauto.com/some/list/page%d/' % page
driver.get(url)
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, 'ul.list > li'))
)
for item in parse_list_page():
data = parse_detail_page(item['link'])
results.append(data)
time.sleep(1) # 减速,降低被风控概率
return results

Oracle 数据入库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import cx_Oracle

def open_db():
db = cx_Oracle.connect('user', 'password', '127.0.0.1:1521/orcl')
cur = db.cursor()
return db, cur

def ensure_table(cur, db):
cur.execute(
"""
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE BITAUTO_DATA (
car_name VARCHAR2(200),
price VARCHAR2(50),
year VARCHAR2(50),
mileage VARCHAR2(50),
city VARCHAR2(100),
link VARCHAR2(400)
)';
EXCEPTION WHEN OTHERS THEN NULL; -- 已存在则忽略
END;
"""
)
db.commit()

def insert_rows(rows):
db, cur = open_db()
ensure_table(cur, db)
cur.executemany(
'INSERT INTO BITAUTO_DATA(car_name, price, year, mileage, city, link) VALUES (:1,:2,:3,:4,:5,:6)',
[(r.get('car_name'), r.get('price'), r.get('year'), r.get('mileage'), r.get('city'), r.get('link')) for r in rows]
)
db.commit()
cur.close(); db.close()

去重策略

触发器示例(按 car_name 去重,保留 rowid 最小的一条):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 建议更稳妥的方式是唯一约束 + MERGE;以下触发器为演示
CREATE OR REPLACE TRIGGER TRI_BIT_BIS_DEL
AFTER INSERT ON BITAUTO_DATA
FOR EACH ROW
BEGIN
DELETE FROM BITAUTO_DATA t
WHERE t.car_name IN (
SELECT car_name FROM BITAUTO_DATA GROUP BY car_name HAVING COUNT(*) > 1
)
AND t.rowid NOT IN (
SELECT MIN(rowid) FROM BITAUTO_DATA GROUP BY car_name HAVING COUNT(*) > 1
);
END;
/

更推荐的方式:

  • car_name, link 创建唯一索引,插入时用 MERGE 或捕获唯一键冲突跳过。

运行组织与错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def run():
try:
login_bitauto()
rows = crawl_pages(max_pages=3)
insert_rows(rows)
except Exception as e:
# 记录错误;必要时截图:driver.save_screenshot('error.png')
import traceback
print traceback.format_exc()
finally:
driver.quit()

if __name__ == '__main__':
run()

调试与总结

  • 超时与稳定性:对关键节点使用 WebDriverWait,设置 set_page_load_timeout,失败重试。
  • 速度控制:详情抓取间隔 sleep(1),必要时在列表批量提取,减少详情打开次数。
  • 选择器维护:优先 CSS 选择器,页面改版时集中更新;正则仅在结构稳定时使用。
  • 数据质量:入库前做 .strip() 清洗;字段标准化(价格单位、里程单位)。
  • 去重与幂等:唯一键或 MERGE 优于触发器;确保重复运行不会膨胀数据。
  • 环境依赖:PhantomJS 已停更,后续可考虑 Chrome Headless(需适配 Python 3 与新版 selenium)。

易车网爬虫 项目总结
https://blog.pangcy.cn/2018/10/29/后端编程相关/python/python2基础/易车网爬虫 项目总结/
作者
子洋
发布于
2018年10月29日
许可协议