Last month the ops team handed me a text export from the CRM: more than 1800 pieces of customer feedback, each preceded by an order number and a ticket number, with courier tracking numbers, email addresses and delivery addresses mixed in. What she wanted was every phone number in there, to check against a campaign list. I started with Notepad and manual copying, managed 400 rows in two hours, and got it wrong twice: once I copied an 11-digit order number as a phone number, and once I skipped a whole line because the number sat right after the word phone with no separator. I changed approach on the third batch.
1. Manual picking fails more often than you would think
| Method | Time for 1800 rows | Errors I could find |
|---|---|---|
| Copying row by row in Notepad | About 2–3 hours | Missed rows, order numbers taken as phone numbers, lines pasted in the wrong place |
| Formulas in spreadsheet software | About 40 minutes | Cannot locate fields glued onto the same line |
| Rule-based bulk extraction plus spot checks | About 5 minutes plus 10 minutes of spot checks | What remains is formatting, visible at a glance |
The difference is not only speed. Manual copying produces the hardest errors to find, the missed row and the wrong row, whereas extraction errors are formatting errors and can be exposed in one pass with sorting and spot checks.
2. Rule-based extraction has boundaries
These tools work by pattern matching, and the usual built-in rules are: a phone number is 11 digits starting with 1, an email is xxx@xxx.xxx, a URL starts with http or https, an IP is four IPv4 octets, an ID number is 18 digits with X allowed at the end, plus plain digits and English words. Once the rules are written that way, false positives and misses are guaranteed:
- False positives: any 11-digit order number, tracking number or barcode fragment starting with 1 is read as a phone number. That is exactly where I crashed.
- Misses: numbers written
138-0013-8000or+86 13800138000have separators and never form 11 digits together; landlines are an area code plus 7 or 8 digits and are not covered by a mobile rule at all. - Historical formats: 15-digit legacy ID numbers do not match the 18-digit rule. Data entered years ago has to be handled separately, or you will conclude that the person left no phone number.
- The plain-digits option cuts both ways: tick it and amounts, reference numbers, dates and quantities all come out, turning 1800 rows of text into thousands of results. If you are not sure which category you want, leave it unticked.
3. What I actually do
- Paste the source text into Text Extractor and tick only phone numbers. That produced 1627 results, and after enabling deduplication 1541 remained, meaning 86 numbers appeared across several pieces of feedback. That gap is information in itself: it says the same customer was entered twice in the list.
- Sort the results by prefix and scan again, removing the ones that do not look like numbers. Long strings starting with
100or1300included a dozen or so order numbers, and I removed 13 by hand. - For the irregular formats, the ones with dashes or an international prefix, I strip the separators in bulk and rerun, which is less work than writing a complicated rule.
- When a rule is uncertain, I check the highlighting and capture groups in Regex Tester first, confirm exactly which segments match, and only then decide whether to run it over everything. For patterns the built-in types do not cover, such as ticket numbers like
WO-2026-0001, I extract all digits or words in one pass and then filter by prefix with bulk find and replace. - Before handing anything over I run Privacy Masking on an external version, masking the middle of each phone number before it goes into the group chat, so a full number does not leave a second copy in the chat history.
4. On logs, the same routine is even easier
Later I used this workflow on an access log investigation. The site was scanned in the early hours, so I cut the suspicious window out of three hours of access.log by time, then extracted IPv4 addresses and got 214 source IPs. Sorted by frequency, 19 IPs accounted for most of the probing of paths like /wp-login.php, /admin/ and .env, while the other 195 were ordinary traffic. The whole thing took under twenty minutes, and the only part that needed a human was sorting and looking at the head of the list.
From the same batch I also pulled URLs, to check that the paths those requests hit were all on the list I expected. When the two extractions disagree, the log field format has usually changed, and that discrepancy is a useful self-check signal, more reliable than trusting the result blindly.
5. A few details that are easy to overlook
- Extraction results are not data truth: a pattern match only says this string looks like a phone number. Joining the results back to the original table needs a field that does not repeat, such as an order number, not a row number, which shifts the moment you sort.
- Think about which record to keep when deduplicating: the same number really can belong to different customers, and a smaller count does not mean the source records can be deleted.
- Where processing happens matters more than feature count: text like customer phone numbers and ID numbers never goes to any backend for me, and local processing is the first thing I check in a tool.
- Slice logs first: pasting tens of MB of access.log into a text box is not realistic. I take one day by time, filter the suspicious window by status code, and only the remainder is a sensible size for extraction.
- Keep three artefacts: the source text, the extraction result and the list of manual removals. When someone later asks why 86 rows are missing, the answer is immediate and nothing has to be rerun.
6. Common questions
Q: Is bulk extraction always accurate?
No. It returns the set that matches the pattern, and both false positives and misses have to be handled with spot checks. At a scale of 1800 rows I check 5 percent, and if the hits cluster under one prefix I check more.
Q: Why was my phone number not extracted?
Nine times out of ten there is a separator inside it or full-width digits were used; occasionally a digit was lost when the text was transcribed and it is short of 11. Normalise the text to half-width, strip the extra symbols and run it again.
Q: Can I extract only emails and not the @ inside links?
Run them separately: tick only email and look at one result, then only URL and look at the other, and comparing the two identifies link fragments that were taken for emails. Ticking several at once only makes the problem harder to locate.
Q: What if I need a custom rule?
The extractor covers the built-in categories; beyond that you have to use a regex to confirm what the pattern looks like, then land it with bulk find and replace filtered by prefix. Do not expect one step to do everything.
Q: What do I do with a pile of duplicates in the results?
Tick deduplication in the extractor. When you also want to see the duplicates, a sort-and-dedupe tool can pull that part out separately, which often surfaces the source of the duplicate entry.
Q: How should results be organised before handing them over?
Keep both the raw result and the deduplicated result, and pass on the removal list as well. I do not group by hand in a spreadsheet; I let a sort-and-dedupe tool surface the duplicates so it is obvious who was entered twice.
Those two hours of manual copying taught me that the real cost of this work is that errors are hard to find, not that copying is slow. Bulk extraction fixes the slow half; the accuracy half comes from spot checks, a removal list and keeping records. Treating it as the first step of the process rather than the whole thing is where it belongs.