Copyfish extension---How to OCR a scrolling webpage?

For a scrolling webpage, now, I have to OCR frame by frame, which is vey inconvenient. How to OCR a scrolling webpage by one frame only?

Copyfish does not have such a feature. Workaround: You can use one of the “Full Page Screenshot” extensions, and then upload the screenshot to the online ocr page.

the extension “Scrolling Screenshot & Full Page Capture” can** Full Page Screenshot and then AI (imge to text).but it only provide 25 screenshots free of charge.

Have you tried using a “Full Page Screenshot” browser extension to capture the entire webpage and then upload the screenshot here for review?

I have tried using a “Full Page Screenshot” browser extension to capture the entire webpage and save the screenshot and then use umi-ocr software to OCR the text, but it is time-consuming. I need to ocr 5 half-page-screenshots everyday. “Scrolling Screenshot & Full Page Capture” is very good but it is not free of charge

You can use the new Ui.Vision V10.0.155 to automate your daily scroll and ocr task for free. For good OCR results use the “Local OCR” option. For best OCR results, get a free OCR API key and select OCR Engine 3.

The below macro scrolls the currently open page. It ocr processes each screenshot and then saves the text. Here is a video of it (original speed, but I removed some “scroll and screenshot” part in the middle to make the video a bit shorter).

Macro source code:

// Scrolling capture + local OCR — full-page text, one slice at a time.
// The macro reads a long page in PARTS half-viewport slices: scroll,
// capture the visible page, OCR only the NEW half of the capture, append.
// Nothing is read twice, and nothing leaves the machine — the reading is
// done by the XModule's Local OCR, so there is no cloud API and no key.
// The showcase call is uiv.ocr.read({image, engine, area}): a STORED
// capture, read by the engine THIS call names, cropped to a region given
// in IMAGE pixels (the capture's own space — a HiDPI shot is bigger than
// its CSS viewport, hence the devicePixelRatio math below).
//
// Needs the RealUser XModule (its Local OCR does the reading).

const vp = JSON.parse(uiv.eval(
  'window.scrollTo(0, 0);' +
  'var el = document.scrollingElement || document.documentElement;' +
  'return JSON.stringify({h: window.innerHeight, w: window.innerWidth,' +
  ' dpr: window.devicePixelRatio,' +
  ' max: Math.max(0, el.scrollHeight - window.innerHeight)});'
));
const half = Math.round(vp.h / 2);
const total = PARTS || Math.ceil((vp.max + vp.h) / half);
uiv.log('Page is ' + (vp.max + vp.h) + ' CSS px tall -> ' + total + ' half-viewport parts.', 'blue');

const parts = [];
for (let i = 0; i < total; i++) {
  const wantY = i * half; // the document row this part starts at
  const gotY = Number(uiv.eval(
    'window.scrollTo(0, ' + wantY + ');' +
    'var el = document.scrollingElement || document.documentElement;' +
    'return el.scrollTop;'
  ));
  // gotY < wantY only when the page ran out and the scroll clamped at the
  // bottom — then the slice sits lower in the viewport, not at its top
  const cssOffset = wantY - gotY;
  if (cssOffset >= vp.h) {
    uiv.log('Page ended after part ' + i + ' — nothing new to read.');
    break;
  }

  // Banner AFTER the capture, and cleared before it: uiv.shot.viewport
  // photographs the banner too (only the visual FINDERS hide it), so showing
  // it first OCRs "OCR part N of 5" into every part (measured). Cleared, shot,
  // then shown — it covers the OCR phase, which is the slow part anyway.
  uiv.banner('');
  const shot = uiv.shot.viewport('ocr_part_' + (i + 1) + '.png');
  uiv.banner('OCR part ' + (i + 1) + ' of ' + total + '…');
  const partH = Math.min(half, vp.h - cssOffset);
  const text = uiv.ocr.read({
    image: shot,
    engine: 'xmodule',
    area: {
      x: 0,
      y: Math.round(cssOffset * vp.dpr),
      width: Math.round(vp.w * vp.dpr),
      height: Math.round(partH * vp.dpr)
    }
  });
  parts.push(String(text).trim());
  uiv.files.remove(shot); // keep the Screenshots tab clean

  // bottom reached and its last half read — later parts would repeat it
  if (gotY >= vp.max && cssOffset + half >= vp.h) break;
}

// --- verify the OCR text, then stitch the parts without duplicate rows ------
// An empty part points at a broken reader, not an empty page.
const emptyParts = [];
for (let i = 0; i < parts.length; i++) { if (!parts[i]) emptyParts.push(i + 1); }
if (emptyParts.length === parts.length) {
  throw new Error('OCR returned no text for any part — is the RealUser XModule (Local OCR) installed?');
}
if (emptyParts.length) {
  uiv.log('Warning: OCR read nothing in part(s) ' + emptyParts.join(', '), 'orange');
}

// A row that already appeared in an EARLIER part is almost always sticky UI
// (header, nav, cookie bar — captured in every slice) or a text row cut by
// the part boundary and read twice. Drop those, and LOG each drop, so a
// legitimate repeat can be checked by hand. Repeats WITHIN one part are
// kept: identical table rows on one screen are real data.
const seen = {};
let dropped = 0;
const out = [];
for (let i = 0; i < parts.length; i++) {
  const kept = [];
  const lines = parts[i].split('\n');
  for (const line of lines) {
    const key = line.replace(/\s+/g, ' ').trim();
    if (!key) continue;
    if (seen[key] !== undefined && seen[key] < i) {
      dropped++;
      uiv.log('Duplicate row dropped (also in part ' + (seen[key] + 1) + '): ' + key.slice(0, 80));
      continue;
    }
    seen[key] = i;
    kept.push(line);
  }
  if (kept.length) out.push('--- part ' + (i + 1) + ' ---\n' + kept.join('\n'));
}
if (dropped) uiv.log(dropped + ' duplicate row(s) removed across part boundaries.', 'orange');

const all = out.join('\n\n') + '\n';


uiv.text.write('ocr-result.txt', all);
uiv.files.exportToDownloads('ocr-result.txt');
uiv.banner('Saved ocr-result.txt (' + out.length + ' parts, ' + dropped + ' duplicate rows removed)', { tone: 'green', seconds: 8 });
uiv.log('Scrolling Capture OCR completed — ocr-result.txt is in the CSV/TXT tab and in Downloads.', 'green');