准备工作
安装 Python 3、PyCharm 社区版
安装 pyautogui 命令如下:(参考:pyautogui安装教程)
pip install pyautogui 或者 pip install pyautogui -i https://mirrors.aliyun.com/pypi/simplepyautogui 使用参考:
https://blog.csdn.net/ibiao/article/details/77859997
https://jingyan.baidu.com/article/39810a23440b20b636fda621.html
技术实现
python + java项目
第一步:抓取所有书的名录,保存到本地数据库
python 脚本
#!/usr/bin/python3 import sys import time import pyautogui from PIL import ImageGrab # 标记图像 imgDir = "D:/dang/python/dang/img" imgLoadingMark = "%s/%s.bmp" % (imgDir, "loadingMark") # “加载中”标记图像 imgLoadFailMark = "%s/%s.bmp" % (imgDir, "loadFailMark") # “加载失败”标记图像 imgLoginPassMark = "%s/%s.bmp" % (imgDir, "loginPassMark") # “登录失效”标记图像 # 参数配置 bookDir = "D:/book/xxxxxxxxx" # 替换书名 bookDir2 = "D:/tmpbmp" # BMP临时目录(用来抓取标记图像) pageCount = 300 # 页数 pageArea = (354, 72, 1012, 950) # 页面区域(x1, y1, x2, y2) # 查找标记图像 def marchMark(fileMark): return pyautogui.locateCenterOnScreen(fileMark, grayscale=True) # 抓取页面图像 def catchImage(area, file): im = ImageGrab.grab(bbox=area) im.save(file) # 检查标记图像 def checkMark(): if markResult := marchMark(imgLoadingMark): print("imgLoadingMark", markResult) time.sleep(3) return checkMark() if markResult := marchMark(imgLoadFailMark): print("imgLoadFailMark", markResult) pyautogui.click(markResult) time.sleep(3) return checkMark() if markResult := marchMark(imgLoginPassMark): print("imgLoginPassMark", markResult) pyautogui.moveTo(markResult) return False return True # 抓取一个页面 def catchPage(i): if checkMark(): # print("catch", i) catchImage(pageArea, "%s/%d.jpg" % (bookDir, i)) catchImage(pageArea, "%s/%d.bmp" % (bookDir2, i)) return True else: return False # 从第 i 页开始抓取 def startImpl(i): while i <= pageCount: if catchPage(i): pyautogui.press('right') # time.sleep(1) i += 1 else: break else: return True return False # 从第 i 页开始抓取 def start(i): if startImpl(i): print("catch success") else: print("catch fail") # 抓取标记图像 def catchMark(area, fileMark): catchImage(area, fileMark) markResult = marchMark(fileMark) if markResult: print("markResult", markResult) pyautogui.moveTo(markResult) print("ready...") time.sleep(3) print("go") start(1) # catchPage(1948) # while True: pyautogui.hotkey('ctrl', 'end') # 下翻操作 # 抓取标记图像 # catchMark((430, 558, 523, 558 + 1), imgLoadingMark) # catchMark((358, 448, 440, 448 + 1), imgLoadFailMark) # catchMark((666, 510, 856, 510 + 1), imgLoginPassMark) # im = pyautogui.screenshot(bookDir + "/1.jpg", region=(0, 0, 300, 400))进入当当电子书的某个分类,例如“自然科学”,运行脚本中的“下翻操作”,翻开所有书目后,手动保存DOM内容为“自然科学.txt”
java项目
pom.xml
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.7.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency> <!--pdf相关--> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.11</version> </dependency> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>fontbox</artifactId> <version>2.0.11</version> </dependency> <!--用于解析html--> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.13.1</version> </dependency>CatchBookTable.java 构建书目记录,方便查找和管理
package com.xnktyu.dangdang; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.xnktyu.utils.*; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.File; import java.io.FileFilter; import java.util.HashMap; import java.util.Map; public class CatchBookTable { private static final DBHelper local_db = new DBHelper("localhost", "root", "xxxxxxx", "dang"); private static class tbase { protected String pack(String field) { return "f_" + field.toLowerCase(); } @Override public String toString() { return getClass().getSimpleName().toLowerCase(); } } private static final class t_book extends tbase { public final String bookId = pack("bookId"); public final String note = pack("note"); public final String title = pack("title"); public final String author = pack("author"); public final String time = pack("time"); public final String vip_link = pack("vip_link"); public final String count_per = pack("count_per"); public final String word_count = pack("word_count"); public final String group = pack("group"); public final String publisher = pack("publisher"); public final String price = pack("price"); public final String url = pack("url"); public final String cover = pack("cover"); public final String des = pack("des"); public final String title_descript = pack("title_descript"); } private static final t_book t_book = new t_book(); private static boolean catchDetail(String url, File dir, JSONObject detail, String bookId) { String text = HttpUtils.doHttpGet(url); if (TextUtils.isEmpty(text)) { LOG.e("error doHttpGet : " + url); return false; } Document doc = Jsoup.parse(text); if (detail == null) detail = new JSONObject(true); detail.put("url", url); detail.put("bookId", bookId); String cover = ""; Elements bookcover_imgs = doc.select(".bookCover_area").select("img"); for (Element img : bookcover_imgs) { if (!img.hasClass("promotion_icon")) { cover = img.attr("src").trim(); } } detail.put("cover", cover); if (doc.select("#author").size() == 0) { LOG.e("error author : " + url); // File file = new File("D:\\wangzhiting\\work\\local\\book\\table\\tmp.txt"); // FsUtils.writeText(file, doc.html()); return false; } detail.put("title", doc.select(".title_words").text().trim()); detail.put("author", doc.select("#author").text().trim().substring("作 者:".length())); detail.put("vip_link", doc.select("#vip_link").size() == 1); detail.put("title_descript", doc.select(".title_descript").text().trim()); detail.put("count_per", doc.select(".count_per").text().trim()); detail.put("publisher", doc.select("#publisher").text().trim().substring("出 版 社:".length())); Elements explain_box_ps = doc.select(".explain_box").select("p"); for (Element p : explain_box_ps) { String str = p.text().trim(); if (str.startsWith("出版时间:")) detail.put("time", str.substring("出版时间:".length())); else if (str.startsWith("字 数:")) detail.put("word_count", str.substring("字 数:".length())); else if (str.startsWith("所属分类: ")) detail.put("group", str.substring("所属分类: ".length())); } // LOGJson.log(detail.toString()); FsUtils.createDir(dir); File file = new File(dir, FsUtils.checkFileName(String.format("[%s]%s.json", detail.getString("bookId"), detail.getString("title")))); FsUtils.writeText(file, detail.toString()); // FsUtils.writeText(file, LOGJson.getStr(detail.toString())); return true; } // 根据DOM解析出书目信息,并保存到本地文件 public static void catchTable(File htmlFile) { LOG.v(htmlFile); File dir = new File(htmlFile.getParentFile(), FsUtils.getNameWithoutSuffix(htmlFile)); String text = FsUtils.readText(htmlFile); if (TextUtils.isEmpty(text)) return; Document doc = Jsoup.parse(text); final JSONArray books = new JSONArray(); // 加载已经解析出来的书目 final Map<String, JSONObject> bookMap = new HashMap<String, JSONObject>(); dir.listFiles(new FileFilter() { public boolean accept(File file) { if (file.isFile()) { if (file.getName().toLowerCase().endsWith(".json")) { String bookId = file.getName().substring("[".length(), file.getName().indexOf("]")); JSONObject book = JsonHelper.getJSONObject(FsUtils.readText(file)); if (book != null) { books.add(book); bookMap.put(bookId, book); } else LOG.e("read fail : " + file); } } return false; } }); Elements book_list = doc.select("#book_list"); Elements alist = book_list.select("a"); LOG.v("alist size : " + alist.size()); for (Element a : alist) { String href = a.attr("href").trim(); if (href.startsWith("./")) href = href.replaceFirst("\\./", "http://e.dangdang.com/"); String bookId = href.substring(href.lastIndexOf("/") + 1, href.lastIndexOf(".")); // 如果没解析过,则执行解析 if (!bookMap.containsKey(bookId)) { LOG.v("catch : " + href); try { Elements bookinfo = a.select(".bookinfo"); String priceStr = bookinfo.select(".now").text().trim(); if (priceStr.equals("免费")) { priceStr = "0"; } else { if (priceStr.startsWith("促销价:")) priceStr = priceStr.substring("促销价:".length()); priceStr = priceStr.substring("¥".length()); } Float price = Float.valueOf(priceStr); String des = bookinfo.select(".des").text().trim(); JSONObject book = new JSONObject(true); book.put("price", price); book.put("des", des); if (catchDetail(href, dir, book, bookId)) { books.add(book); bookMap.put(bookId, book); } Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } } } // File file = new File(htmlFile.getParentFile(), String.format("%s.json", FsUtils.getNameWithoutSuffix(htmlFile))); // FsUtils.writeText(file, books.toString()); // FsUtils.writeText(file, LOGJson.getStr(books.toString())); LOG.v("books size : " + books.size()); LOG.v("bookMap size : " + bookMap.size()); } private static Integer parseCount(String countStr) { if (!TextUtils.isEmpty(countStr)) { if (countStr.endsWith("万")) { countStr = countStr.substring(0, countStr.length() - "万".length()); return (int) (Float.valueOf(countStr) * 10000); } else return Integer.valueOf(countStr); } return 0; } // 将解析出的数据保存到数据库,方便检索 public static void uploadRecord(File htmlFile) { LOG.v(htmlFile); File dir = new File(htmlFile.getParentFile(), FsUtils.getNameWithoutSuffix(htmlFile)); local_db.exeSql(String.format("create table if not exists %s(" + t_book.bookId + " varchar(100)" + // ", " + t_book.note + " text" + // ", " + t_book.title + " text" + // ", " + t_book.author + " text" + // ", " + t_book.time + " date" + // ", " + t_book.vip_link + " tinyint" + // ", " + t_book.count_per + " int" + // ", " + t_book.word_count + " int" + // ", " + t_book.group + " text" + // ", " + t_book.publisher + " text" + // ", " + t_book.price + " float" + // ", " + t_book.url + " text" + // ", " + t_book.cover + " text" + // ", " + t_book.des + " text" + // ", " + t_book.title_descript + " text" + // ");", t_book), null); dir.listFiles(new FileFilter() { public boolean accept(File file) { if (file.isFile()) { if (file.getName().toLowerCase().endsWith(".json")) { JSONObject book = JsonHelper.getJSONObject(FsUtils.readText(file)); if (book != null) { if (!local_db.hasRecord(t_book, t_book.bookId, book.getString("bookId"))) { String time = book.getString("time"); if (TextUtils.isEmpty(time)) time = "1970-01-01"; String count_per = book.getString("count_per"); boolean success = local_db.insert(t_book, // t_book.bookId, book.getString("bookId"), // t_book.title, book.getString("title"), // t_book.author, book.getString("author"), // t_book.time, time, // t_book.vip_link, book.getBoolean("vip_link") ? 1 : 0, // t_book.count_per, parseCount(count_per.substring(0, count_per.indexOf("人正在读"))), // t_book.word_count, parseCount(book.getString("word_count")), // t_book.group, book.getString("group"), // t_book.publisher, book.getString("publisher"), // t_book.price, book.getString("price"), // t_book.url, book.getString("url"), // t_book.cover, book.getString("cover"), // t_book.des, book.getString("des"), // t_book.title_descript, book.getString("title_descript")); if (!success) { LOGJson.log(book.toString()); } } } else LOG.e("read fail : " + file); } } return false; } }); } // 修改数据库中的记录备注信息 public static void updateNote(String bookId, String note) { local_db.update(t_book, DBHelper.set(t_book.note, note), t_book.bookId, bookId); } }运行 catchTable 解析出书目信息,并保存到本地文件
catchTable(new File("自然科学.txt"));确认无误后,运行 uploadRecord 将解析出的数据保存到数据库,方便检索
uploadRecord(new File("自然科学.txt"));使用 SQLyog 进入数据库,检索可以下载的书
SELECT * FROM `t_book` WHERE `f_vip_link` = 1 AND `f_group` LIKE '%%' AND `f_title` LIKE '%科学%' ORDER BY `f_time` DESC第二步:抓取某本书目录信息
在数据库找到要抓取的书ID,根据ID,打开阅读页面。手动保存页面DOM到临时文件 tmp.html
CatchBookDir.java 解析目录,生成目录文件mark.json并标记数据库备注信息
package com.xnktyu.dangdang; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.xnktyu.utils.FsUtils; import com.xnktyu.utils.LOG; import com.xnktyu.utils.LOGJson; import com.xnktyu.utils.TextUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.File; public class CatchBookDir { private static JSONArray genDirData(Elements cata_list) { JSONArray dirs = new JSONArray(); Elements lis = cata_list.select("li"); for (Element li : lis) { String level = li.attr("class"); Elements a = li.select("a").eq(0); String pagecount = a.attr("pagecount"); String name = a.text().trim(); name = name.substring(0, name.length() - pagecount.length()); JSONObject dir = new JSONObject(true); dir.put("level", Integer.valueOf(level.substring("level".length()))); dir.put("name", name); dir.put("page", Integer.valueOf(pagecount)); dirs.add(dir); } return dirs; } // 解析目录,生成目录文件mark.json并标记数据库备注信息 public static void catchDir(File htmlFile, File dir) { String text = FsUtils.readText(htmlFile); if (TextUtils.isEmpty(text)) return; Document doc = Jsoup.parse(text); // FsUtils.createDir(dir); // File tmpFile = new File(dir, "tmp.html"); // FsUtils.writeText(tmpFile, doc.html()); String href = doc.select("#ddclick-tool-left-book-link").attr("href").trim(); String bookId = href.substring(href.lastIndexOf("/") + 1, href.lastIndexOf(".")); Elements other_out = doc.select(".other-out").eq(0); Elements title = other_out.select(".title").eq(0); String bookName = FsUtils.checkFileName(title.text().trim()); LOG.v(bookId + bookName); File bookDir = new File(dir, bookName); FsUtils.createDir(bookDir); Elements cata_list = other_out.select(".cata-list").eq(0); File markFile = new File(bookDir, "mark.json"); JSONObject book = new JSONObject(true); book.put("bookId", bookId); book.put("name", bookName); book.put("dirs", genDirData(cata_list)); FsUtils.writeText(markFile, LOGJson.getStr(book.toString(), 2)); CatchBookTable.updateNote(bookId, "added"); } }调用示例:
catchDir(new File("tmp.html"), new File("D:\\book"));第三步:抓取某本书页面图片
在阅读页面,设置好脚本的“参数配置”,执行 python 脚本的 start(1),如果中断,可调整参数继续执行。
注意:脚本运行前,需要通过 catchMark 配置好“标记图像”,“标记图像”的作用是检查页面错误,如果配置后仍然发现有页面错误的情况,可参照抓取过程中 bookDir2 下的位图信息查找原因,并调整“标记图像”。“标记图像”需要保存为bmp
第四步:生成pdf
Image2Pdf.java
package com.xnktyu.image2pdf; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.xnktyu.dangdang.CatchBookTable; import com.xnktyu.utils.FsUtils; import com.xnktyu.utils.JsonHelper; import com.xnktyu.utils.LOGJson; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * 这个类可以将图片转换为pdf */ public class Image2Pdf { /** * 为pdf添加书签 * * @param bookmark * @param dirTree */ private static void addBookmark(PDOutlineNode bookmark, JSONArray dirTree) { for (int i = 0; i < dirTree.size(); i++) { JSONObject dirNode = dirTree.getJSONObject(i); PDOutlineItem item = new PDOutlineItem(); item.setTitle(dirNode.getString("name")); PDPageXYZDestination dest = new PDPageXYZDestination(); dest.setPageNumber(dirNode.getInteger("page") - 1); item.setDestination(dest); bookmark.addLast(item); addBookmark(item, dirNode.getJSONArray("dirTree")); } } private static String getBookId(File bookDir) { File markFile = new File(bookDir, "mark.json"); if (markFile.exists()) { JSONObject book = JsonHelper.getJSONObject(FsUtils.readText(markFile)); return book.getString("bookId"); } return null; } /** * 根据mark.json添加书签 * * @param bookDir * @param pageOffset * @param outline */ private static void addMark(File bookDir, Integer pageOffset, PDDocumentOutline outline) { JSONObject book; File markFile = new File(bookDir, "mark.json"); if (markFile.exists()) { book = JsonHelper.getJSONObject(FsUtils.readText(markFile)); } else { book = new JSONObject(true); JSONArray dirs = new JSONArray(); JSONObject dir = new JSONObject(true); dir.put("level", 0); dir.put("name", "封面"); dir.put("page", 1); dirs.add(dir); book.put("dirs", dirs); } FsUtils.writeText(markFile, LOGJson.getStr(book.toString(), 2)); JSONArray dirTree = new JSONArray(); JSONObject dirNode0 = null, dirNode1 = null, dirNode2 = null, dirNode3 = null; JSONArray dirTree0 = null, dirTree1 = null, dirTree2 = null, dirTree3 = null; JSONArray dirs = book.getJSONArray("dirs"); for (int i = 0; i < dirs.size(); i++) { JSONObject dir = dirs.getJSONObject(i); if (dir.getInteger("level") == 0) { dirNode0 = new JSONObject(true); dirTree0 = new JSONArray(); dirNode0.put("name", dir.getString("name")); dirNode0.put("page", dir.getInteger("page") + pageOffset); dirNode0.put("dirTree", dirTree0); dirTree.add(dirNode0); } else if (dir.getInteger("level") == 1) { dirNode1 = new JSONObject(true); dirTree1 = new JSONArray(); dirNode1.put("name", dir.getString("name")); dirNode1.put("page", dir.getInteger("page") + pageOffset); dirNode1.put("dirTree", dirTree1); dirTree0.add(dirNode1); } else if (dir.getInteger("level") == 2) { dirNode2 = new JSONObject(true); dirTree2 = new JSONArray(); dirNode2.put("name", dir.getString("name")); dirNode2.put("page", dir.getInteger("page") + pageOffset); dirNode2.put("dirTree", dirTree2); dirTree1.add(dirNode2); } else if (dir.getInteger("level") == 3) { dirNode3 = new JSONObject(true); dirTree3 = new JSONArray(); dirNode3.put("name", dir.getString("name")); dirNode3.put("page", dir.getInteger("page") + pageOffset); dirNode3.put("dirTree", dirTree3); dirTree2.add(dirNode3); } } addBookmark(outline, dirTree); } /** * 根据图片,生成pdf文件 * * @param bookDir 存放图片的目录,文件名按数字排序,如没有书签文件mark.json,会创建默认书签文件,对书签文件手动编辑后,再调用此方法 * @param pageOffset 书签页码偏移,编辑书签的时候,可以参考目录图片编辑,但目录图片的书签名往往跟文件不对应,可用此偏移矫正 */ public static void convert(File bookDir, Integer pageOffset) { if (!bookDir.exists()) return; if (!bookDir.isDirectory()) return; String bookName = bookDir.getName(); File pdfDir = new File(bookDir.getParentFile(), "pdf"); FsUtils.createDir(pdfDir); File outFile = new File(pdfDir, bookName + ".pdf"); if (outFile.exists()) { CatchBookTable.updateNote(getBookId(bookDir), "catched"); return; } final List<File> fileList = new ArrayList<File>(); bookDir.listFiles(new FileFilter() { public boolean accept(File file) { if (file.isFile()) { if (file.getName().toLowerCase().endsWith(".jpg") || file.getName().toLowerCase().endsWith(".jpeg") || file.getName().toLowerCase().endsWith(".png")) { fileList.add(file); } } return false; } }); if (fileList.size() == 0) return; Collections.sort(fileList, new Comparator<File>() { public int compare(File file1, File file2) { String name1 = file1.getName().substring(0, file1.getName().lastIndexOf(".")); String name2 = file2.getName().substring(0, file2.getName().lastIndexOf(".")); try { Integer num1 = Integer.valueOf(name1); Integer num2 = Integer.valueOf(name2); return num1.compareTo(num2); } catch (Exception e) { return name1.compareTo(name2); } } }); try { PDDocument document = new PDDocument(); for (int i = 0; i < fileList.size(); i++) { File file = fileList.get(i); PDPage page = new PDPage(); PDImageXObject image = PDImageXObject.createFromFile(file.getAbsolutePath(), document); page.setMediaBox(new PDRectangle(image.getWidth(), image.getHeight())); PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true); float scale = 1f; contentStream.drawImage(image, 0, 0, image.getWidth() * scale, image.getHeight() * scale); contentStream.close(); document.addPage(page); } PDDocumentOutline outline = new PDDocumentOutline(); addMark(bookDir, pageOffset, outline); document.getDocumentCatalog().setDocumentOutline(outline); document.save(outFile); document.close(); CatchBookTable.updateNote(getBookId(bookDir), "catched"); } catch (IOException e) { e.printStackTrace(); } } }调用示例:
public static void genPdf(File dir) { dir.listFiles(new FileFilter() { public boolean accept(File file) { if (file.isDirectory() && !file.getName().equals("pdf") && !file.getName().equals("table")) { LOG.v(file); Image2Pdf.convert(file, 0); } return false; } }); } public static void main(String args[]) { genPdf(new File("D:\\book")); }这个调用会根据之前生成的目录信息(mark.json)和抓取的页面图片,生成PDF。
重复第二、三、四步,抓取其它书。
另外,仅限PC阅读的书需要调整 python 脚本,并且没有目录。以下是脚本参考:
#!/usr/bin/python3 import sys import time import pyautogui from PIL import ImageGrab imgDir = "D:/dang/python/dang/img" imgLoadFailMark = "%s/%s.bmp" % (imgDir, "loadFailMark") imgLoginPassMark = "%s/%s.bmp" % (imgDir, "loginPassMark") screenWidth, screenHeight = pyautogui.size() bookDir = "D:/book/XXXXXXX" bookDir2 = "D:/book/tmpbmp" pageCount = 196 pageLeftArea = (186, 0, 186 + 770, screenHeight) pageRightArea = (962, 0, 962 + 770, screenHeight) def marchMark(fileMark): return pyautogui.locateCenterOnScreen(fileMark, grayscale=True) def catchImage(area, file): im = ImageGrab.grab(bbox=area) im.save(file) def checkMark(): markResult = marchMark(imgLoadFailMark) if markResult: print("imgLoadFailMark", markResult) pyautogui.click(markResult) time.sleep(1) return checkMark() markResult = marchMark(imgLoginPassMark) if markResult: print("imgLoginPassMark", markResult) pyautogui.moveTo(markResult) return False return True def isLeftPage(i): return i % 2 == 0 def catchPage(i): if isLeftPage(i): if checkMark(): # print("catch", i) catchImage(pageLeftArea, "%s/%d.jpg" % (bookDir, i)) catchImage(pageLeftArea, "%s/%d.bmp" % (bookDir2, i)) return True else: return False else: if checkMark(): # print("catch", i) catchImage(pageRightArea, "%s/%d.jpg" % (bookDir, i)) catchImage(pageRightArea, "%s/%d.bmp" % (bookDir2, i)) return True else: return False def startImpl(i): while i <= pageCount: if catchPage(i): if isLeftPage(i): i += 1 else: pyautogui.press('right') time.sleep(1) i += 1 else: break else: return True return False def start(i): if startImpl(i): print("catch success") else: print("catch fail") def catchMark(area, fileMark): catchImage(area, fileMark) markResult = marchMark(fileMark) if markResult: print("markResult", markResult) pyautogui.moveTo(markResult) print("ready...") time.sleep(3) print("go") start(1) # catchPage(xxx) # catchMark((358, 448, 440, 448 + 1), imgLoadFailMark) # catchMark((666, 510, 856, 510 + 1), imgLoginPassMark)